370 lines
12 KiB
Lua
370 lines
12 KiB
Lua
-- global callbacks
|
|
trail = {}
|
|
|
|
-- Parameters
|
|
|
|
local FOO = true -- Enable footprints.
|
|
local FUNCYC = 0.2 -- Function cycle in seconds.
|
|
|
|
local TRACHA = minetest.settings:get("trail_hardpack_chance") or 0.1 -- Chance walked dirt/grass is worn and compacted to trail:trail.
|
|
local ICECHA = minetest.settings:get("trail_ice_chance") or 0.05 -- Chance walked snowblock is compacted to ice.
|
|
local EROSION = minetest.settings:get_bool("trail_erosion", true) -- Enable footprint erosion.
|
|
local TRAIL_EROSION = minetest.settings:get_bool("trail_trail_erosion", true) -- Allow hard-packed soil to erode back to dirt
|
|
local EROINT = minetest.settings:get("trail_erosion_interval") or 16 -- Erosion interval.
|
|
local EROCHA = minetest.settings:get("trail_erosion_chance") or 128 -- Erosion 1/x chance.
|
|
|
|
|
|
-- Utility
|
|
|
|
local function deep_copy(input)
|
|
if type(input) ~= "table" then
|
|
return input
|
|
end
|
|
local output = {}
|
|
for index, value in pairs(input) do
|
|
output[index] = deep_copy(value)
|
|
end
|
|
return output
|
|
end
|
|
|
|
-- Player positions
|
|
|
|
local player_pos_previous = {}
|
|
|
|
minetest.register_on_joinplayer(function(player)
|
|
player_pos_previous[player:get_player_name()] = {x = 0, y = 0, z = 0}
|
|
end)
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
player_pos_previous[player:get_player_name()] = nil
|
|
end)
|
|
|
|
local trails = {}
|
|
local erosion = {}
|
|
|
|
trail.register_trample_node = function(trampleable_node_name, trample_def)
|
|
if trails[trampleable_node_name] then
|
|
minetest.log("error", "[trail] Attempted to call trail.register_trample_node to register trampleable node "
|
|
.. trampleable_node_name ..", which has already been registered as trampleable.")
|
|
return
|
|
end
|
|
local trampleable_node_def = minetest.registered_nodes[trampleable_node_name]
|
|
if trampleable_node_def == nil then
|
|
minetest.log("error", "[trail] Attempted to call trail.register_trample_node with the trampleable node "
|
|
.. trampleable_node_name ..", which has not yet been registered as a node.")
|
|
return
|
|
end
|
|
|
|
local trampled_node_name = trample_def.trampled_node_name or trampleable_node_name.."_trampled"
|
|
if not minetest.registered_nodes[trampled_node_name] then
|
|
local trampled_node_def = deep_copy(trampleable_node_def) -- start with a deep copy of the source node's definition
|
|
if trample_def.trampled_node_def_override then -- override any values that need to be overridden explicitly
|
|
for key, value in pairs(trample_def.trampled_node_def_override) do
|
|
trampled_node_def[key] = value
|
|
end
|
|
end
|
|
|
|
-- Set up the erosion ABM group
|
|
if EROSION and trample_def.erodes ~= false then
|
|
local groups = trampled_node_def.groups or {}
|
|
groups.trail_erodes = 1
|
|
trampled_node_def.groups = groups
|
|
erosion[trampled_node_name] = trampleable_node_name
|
|
end
|
|
|
|
-- If the source node doesn't have a special drop, then set drop to drop a source node rather than dropping a node with a footstep.
|
|
if trampled_node_def.drop == nil then
|
|
trampled_node_def.drop = trampleable_node_name
|
|
end
|
|
|
|
-- Modify the +Y tile with a footprint overlay
|
|
if trample_def.add_footprint_overlay ~= false then
|
|
local tiles = trampled_node_def.tiles
|
|
local first_tile = tiles[1]
|
|
local second_tile = tiles[2]
|
|
if second_tile == nil then
|
|
-- The provided node def only has one tile for all sides. We need to only modify the +Y tile,
|
|
-- so we need to copy the original first tile into the second tile slot
|
|
tiles[2] = deep_copy(first_tile)
|
|
end
|
|
if type(first_tile) == "table" then
|
|
first_tile.name = first_tile.name .. "^default_footprint.png"
|
|
elseif type(first_tile) == "string" then
|
|
first_tile = first_tile .. "^default_footprint.png"
|
|
end
|
|
trampled_node_def.tiles[1] = first_tile
|
|
end
|
|
|
|
minetest.register_node(":"..trampled_node_name, trampled_node_def)
|
|
|
|
-- If hard pack has been defined for this trail type, add it
|
|
local hard_pack_node_name = trample_def.hard_pack_node_name
|
|
if hard_pack_node_name then
|
|
local hard_pack_probability = trample_def.hard_pack_probability or 0.1
|
|
trails[trampled_node_name] = {name=hard_pack_node_name, probability=hard_pack_probability}
|
|
end
|
|
end
|
|
|
|
local probability = trample_def.probability or 1
|
|
trails[trampleable_node_name] = {name=trampled_node_name, probability=probability, randomize_trampled_param2 = trample_def.randomize_trampled_param2,}
|
|
end
|
|
|
|
trail.register_erosion = function(source_node_name, destination_node_name)
|
|
if not EROSION then
|
|
return
|
|
end
|
|
|
|
if minetest.registered_nodes[source_node_name] == nil then
|
|
minetest.log("error", "[trail] attempted to call trail.register_erosion with unregistered source node "
|
|
.. source_node_name)
|
|
return
|
|
end
|
|
if minetest.registered_nodes[destination_node_name] == nil then
|
|
minetest.log("error", "[trail] attempted to call trail.register_erosion with unregistered destination node "
|
|
.. destination_node_name)
|
|
return
|
|
end
|
|
if minetest.get_item_group(source_node_name, "trail_erodes") == 0 then
|
|
minetest.log("error", "[trail] attempted to call trail.register_erosion with source node "
|
|
.. destination_node_name .. " that wasn't in group trail_erodes.")
|
|
return
|
|
end
|
|
|
|
erosion[source_node_name] = destination_node_name
|
|
end
|
|
|
|
-- Nodes
|
|
|
|
-- hard-packed soil
|
|
local trail_trail_def = {
|
|
tiles = {"trail_trailtop.png", "default_dirt.png",
|
|
"default_dirt.png^trail_trailside.png"},
|
|
groups = {crumbly = 2},
|
|
drop = "default:dirt",
|
|
sounds = default.node_sound_dirt_defaults(),
|
|
}
|
|
if TRAIL_EROSION then
|
|
trail_trail_def.groups.trail_erodes = 1
|
|
end
|
|
minetest.register_node("trail:trail", trail_trail_def)
|
|
if TRAIL_EROSION then
|
|
trail.register_erosion("trail:trail", "default:dirt")
|
|
end
|
|
|
|
-- Default dirt
|
|
|
|
trail.register_trample_node("default:dirt", {
|
|
trampled_node_name = "trail:dirt",
|
|
trampled_node_def_override = {description = "Dirt with Footprint",},
|
|
hard_pack_node_name = "trail:trail",
|
|
hard_pack_probability = TRACHA,
|
|
})
|
|
|
|
trail.register_trample_node("default:dirt_with_grass", {
|
|
trampled_node_name = "trail:dirt_with_grass",
|
|
trampled_node_def_override = {description = "Dirt with Grass and Footprint",},
|
|
hard_pack_node_name = "trail:trail",
|
|
hard_pack_probability = TRACHA,
|
|
})
|
|
|
|
trail.register_trample_node("default:dirt_with_dry_grass", {
|
|
trampled_node_name = "trail:dirt_with_dry_grass",
|
|
trampled_node_def_override = {description = "Dirt with Dry Grass and Footprint",},
|
|
hard_pack_node_name = "trail:trail",
|
|
hard_pack_probability = TRACHA,
|
|
})
|
|
|
|
trail.register_trample_node("default:dirt_with_snow", {
|
|
trampled_node_name = "trail:dirt_with_snow",
|
|
trampled_node_def_override = {description = "Dirt with Snow and Footprint",},
|
|
hard_pack_node_name = "trail:trail",
|
|
hard_pack_probability = TRACHA,
|
|
})
|
|
|
|
trail.register_trample_node("default:dirt_with_rainforest_litter", {
|
|
trampled_node_name = "trail:dirt_with_rainforest_litter",
|
|
trampled_node_def_override = {description = "Dirt with Rainforest Litter and Footprint",},
|
|
hard_pack_node_name = "trail:trail",
|
|
hard_pack_probability = TRACHA,
|
|
})
|
|
|
|
trail.register_trample_node("default:dirt_with_coniferous_litter", {
|
|
trampled_node_name = "trail:dirt_with_coniferous_litter",
|
|
trampled_node_def_override = {description = "Dirt with Coniferous Litter and Footprint",},
|
|
hard_pack_node_name = "trail:trail",
|
|
hard_pack_probability = TRACHA,
|
|
})
|
|
|
|
-- Default sand
|
|
|
|
trail.register_trample_node("default:sand", {
|
|
trampled_node_name = "trail:sand",
|
|
trampled_node_def_override = {description = "Sand with Footprint",},
|
|
})
|
|
|
|
trail.register_trample_node("default:desert_sand", {
|
|
trampled_node_name = "trail:desert_sand",
|
|
trampled_node_def_override = {description = "Desert Sand with Footprint",},
|
|
})
|
|
|
|
trail.register_trample_node("default:silver_sand", {
|
|
trampled_node_name = "trail:silver_sand",
|
|
trampled_node_def_override = {description = "Silver Sand with Footprint",},
|
|
})
|
|
|
|
trail.register_trample_node("default:gravel", {
|
|
trampled_node_name = "trail:gravel",
|
|
trampled_node_def_override = {description = "Gravel with Footprint",},
|
|
})
|
|
|
|
-- Default snow
|
|
|
|
trail.register_trample_node("default:snowblock", {
|
|
trampled_node_name = "trail:snowblock",
|
|
trampled_node_def_override = {description = "Snow Block with Footprint",},
|
|
hard_pack_node_name = "default:ice",
|
|
hard_pack_probability = ICECHA,
|
|
})
|
|
|
|
trail.register_trample_node("default:snow", {
|
|
trampled_node_name = "trail:snow",
|
|
trampled_node_def_override = {description = "Snow with Footprint",},
|
|
})
|
|
|
|
|
|
--[[if minetest.get_modpath("farming") then
|
|
-- Flattened wheat
|
|
minetest.register_node("trail:wheat", {
|
|
description = "Flattened Wheat",
|
|
tiles = {"trail_flat_wheat.png"},
|
|
inventory_image = "trail_flat_wheat.png",
|
|
drawtype = "nodebox",
|
|
paramtype = "light",
|
|
paramtype2 = "facedir",
|
|
buildable_to = true,
|
|
node_box = {
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.5, -0.5, -0.5, 0.5, -3 / 8, 0.5}
|
|
},
|
|
},
|
|
groups = {snappy = 3, flammable = 2, attached_node = 1},
|
|
drop = "",
|
|
sounds = default.node_sound_leaves_defaults(),
|
|
})
|
|
|
|
trail.register_trample_node("farming:wheat_5", {
|
|
trampled_node_name = "trail:wheat",
|
|
randomize_trampled_param2 = true,
|
|
})
|
|
trail.register_trample_node("farming:wheat_6", {
|
|
trampled_node_name = "trail:wheat",
|
|
randomize_trampled_param2 = true,
|
|
})
|
|
trail.register_trample_node("farming:wheat_7", {
|
|
trampled_node_name = "trail:wheat",
|
|
randomize_trampled_param2 = true,
|
|
})
|
|
trail.register_trample_node("farming:wheat_8", {
|
|
trampled_node_name = "trail:wheat",
|
|
randomize_trampled_param2 = true,
|
|
})
|
|
end
|
|
--]]
|
|
-- Globalstep function
|
|
|
|
if FOO then
|
|
local timer = 0
|
|
|
|
local get_param2 = function(trail_def)
|
|
if trail_def.randomize_trampled_param2 then
|
|
return math.random(0,3)
|
|
end
|
|
return 0
|
|
end
|
|
|
|
minetest.register_globalstep(function(dtime)
|
|
timer = timer + dtime
|
|
if timer > FUNCYC then
|
|
timer = 0
|
|
for _, player in ipairs(minetest.get_connected_players()) do
|
|
local pos = player:get_pos()
|
|
local player_name = player:get_player_name()
|
|
local current_player_pos = {
|
|
x = math.floor(pos.x + 0.5),
|
|
y = math.floor(pos.y + 0.2),
|
|
z = math.floor(pos.z + 0.5)
|
|
}
|
|
|
|
--if player_pos_previous[player_name] == nil then
|
|
--break
|
|
--end
|
|
|
|
if current_player_pos.x ~= player_pos_previous[player_name].x or
|
|
current_player_pos.y < player_pos_previous[player_name].y or
|
|
current_player_pos.z ~= player_pos_previous[player_name].z then
|
|
|
|
local p_snow = {
|
|
x = math.floor(pos.x + 0.5),
|
|
y = math.floor(pos.y + 1.2),
|
|
z = math.floor(pos.z + 0.5)
|
|
}
|
|
local n_snow = minetest.get_node(p_snow).name
|
|
|
|
-- test ground cover first (snow, wheat)
|
|
local trail_def = trails[n_snow]
|
|
if trail_def then
|
|
if math.random() <= trail_def.probability then
|
|
local p_snowpl = {
|
|
x = math.floor(pos.x + 0.5),
|
|
y = math.floor(pos.y + 0.5),
|
|
z = math.floor(pos.z + 0.5)
|
|
}
|
|
minetest.set_node(p_snowpl, {name = trail_def.name, param2 = get_param2(trail_def)})
|
|
end
|
|
else
|
|
local p_ground = {
|
|
x = math.floor(pos.x + 0.5),
|
|
y = math.floor(pos.y + 0.4),
|
|
z = math.floor(pos.z + 0.5)
|
|
}
|
|
local n_ground = minetest.get_node(p_ground).name
|
|
trail_def = trails[n_ground]
|
|
if trail_def and math.random() <= trail_def.probability then
|
|
local p_groundpl = {
|
|
x = math.floor(pos.x + 0.5),
|
|
y = math.floor(pos.y - 0.25),
|
|
z = math.floor(pos.z + 0.5)
|
|
}
|
|
minetest.set_node(p_groundpl, {name = trail_def.name, param2 = get_param2(trail_def)})
|
|
end
|
|
end
|
|
end
|
|
|
|
player_pos_previous[player_name] = current_player_pos
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
-- ABM
|
|
|
|
if EROSION then
|
|
minetest.register_abm({
|
|
nodenames = {"group:trail_erodes"},
|
|
interval = EROINT,
|
|
chance = EROCHA,
|
|
catch_up = true,
|
|
action = function(pos, node, _, _)
|
|
local nodename = node.name
|
|
local erodes_to = erosion[nodename]
|
|
if erodes_to then
|
|
minetest.set_node(pos, {name = erodes_to})
|
|
else
|
|
minetest.log("error", "[trail] The node " .. nodename .. " is in group trail_erodes but "
|
|
.. " didn't have an erosion target node defined.")
|
|
end
|
|
end
|
|
})
|
|
end
|