poschangelib/stomping.lua

231 lines
8.1 KiB
Lua
Raw Normal View History

2017-08-22 19:33:18 +02:00
--[[
This file contains the stomping layer.
It is dedicated to transform a node to an other when walked on.
--]]
local function table_copy(table)
local orig_type = type(table)
local copy = {}
if orig_type ~= 'table' then return table end
for orig_key, orig_value in next, table, nil do
copy[orig_key] = table_copy(orig_value)
end
return copy
end
--- Store all registered stomped nodes indexed by source node name
-- For every node name there can be a list of stomping descriptions, ordered
-- by priority in ascending order.
local stomps = {}
--- Get default stomp name to use with listeners.
local function get_stomp_name(source_node_name, stomp_node_name, mod_name)
if not mod_name then
mod_name = minetest.get_current_modname()
end
return mod_name .. ':' .. source_node_name .. '__to__' .. stomp_node_name
end
function poschangelib.get_footprint_node_name(source_node_name, mod_name)
if not mod_name then
-- current_modname is the caller mod, not always poschangelib
mod_name = minetest.get_current_modname()
end
node_mod_name = string.sub(source_node_name, 1, string.find(source_node_name, ':'))
if node_mode_name == mod_name then
return source_node_name .. '_with_footprint'
else
return mod_name .. ':' .. string.gsub(source_node_name, ':', '__') .. '_with_footprint'
end
end
--- poschangelib walk callback
local function walk_listener(player, pos, node, desc)
poschangelib.chance_stomp(player, pos, node, node_desc)
end
--- Random roll and do the stomp if it succeeds.
function poschangelib.chance_stomp(player, pos, node, node_desc)
local stomp_desc = stomps[node.name]
if not stomp_desc then
minetest.log('warning', 'No stomping data found for node ' .. node.name)
return
end
for i, s_desc in ipairs(stomp_desc) do
if (math.random() * s_desc.chance) < 1.0 then
poschangelib.do_stomp(player, pos, node, node_desc, s_desc)
return
end
end
end
--- Actually do the stomp: replace the stomped node.
-- @param player The player the triggered the stomp.
-- @param pos Position of the stomped node.
-- @param node Node being stomped.
-- @param node_desc Description of the node being stomped.
-- @param stomp_desc Optional stomp description. If not provided it looks for it.
function poschangelib.do_stomp(player, pos, node, node_desc, stomp_desc)
if not stomp_desc then
stomp_desc = stomps[node.name]
if stomp_desc then stomp_desc = stomp_desc[1] end
end
if not stomp_desc then
minetest.log('warning', 'No stomping data found for node ' .. node.name)
return
end
if type(stomp_desc.dest_node_name) == 'function' then
local dest_node = stomp_desc.dest_node_name(player, pos, node, trigger_meta)
if not dest_node then return end
minetest.set_node(pos, dest_node)
else
minetest.set_node(pos, {name = stomp_desc.dest_node_name})
end
end
--[[
-- Revert timer, used in node registration.
--]]
function poschangelib.change_node(pos, stomped_node_name, reverted_node_name)
-- Check if the node is still the right one
local node = minetest.get_node(pos)
if (node.name ~= stomped_node_name) then return end
-- Replace it
minetest.set_node(pos, {name = reverted_node_name})
end
--[[
-- Node registration
--]]
--- Set the default values for a stomp_desc.
-- stomp_desc.dest_node_name must be set.
local function stomp_desc_defaults(source_node_name, stomp_desc)
if not stomp_desc.chance then stomp_desc.chance = 1 end
if not stomp_desc.source_node then
stomp_desc.source_node = source_node_name
end
if not stomp_desc.priority then stomp_desc.priority = 100 end
if stomp_desc.duration then
stomp_desc.duration_min = stomp_desc.duration
stomp_desc.duration_max = stomp_desc.duration
stomp_desc.duration = nil
end
if not stomp_desc.priority then stomp_desc.priority = 100 end
if not stomp_desc.name then
stomp_desc.name = get_stomp_name(source_node_name, stomp_desc.dest_node_name)
end
end
--- Register a footprinted version of a node
function poschangelib.register_footprints(node_name, stomp_desc)
local desc = minetest.registered_nodes[node_name]
if not desc then
minetest.log('error', 'Trying to register footprints for unknow node ' .. node_name)
return
end
local stomped_node_name = poschangelib.get_footprint_node_name(node_name)
stomp_desc.dest_node_name = stomped_node_name
stomp_desc_defaults(node_name, stomp_desc)
local stomped_node_desc = table_copy(desc)
stomped_node_desc.description = desc.description .. ' With Footprint'
-- Add footprint on top of the node texture
local footprint_texture = 'poschangelib_footprint.png'
if stomp_desc.footprint_texture then
footprint_texture = stomp_desc.footprint_texture
end
if type(desc.tiles[1]) == 'table' then
stomped_node_desc.tiles[1].name = desc.tiles[1].name .. '^' .. footprint_texture
else
stomped_node_desc.tiles[1] = desc.tiles[1] .. '^' .. footprint_texture
end
-- Revert timer
if stomp_desc.duration_min then
if not desc.on_timer then
stomped_node_desc.on_timer = function(pos, elapsed)
poschangelib.change_node(pos, stomped_node_name, node_name)
end
end
if desc.on_construct then
stomped_node_desc.on_construct = function(pos)
desc.on_construct(pos)
minetest.get_node_timer(pos):start(math.random(stomp_desc.duration_min, stomp_desc.duration_max))
end
else
stomped_node_desc.on_construct = function(pos) minetest.get_node_timer(pos):start(math.random(stomp_desc.duration_min, stomp_desc.duration_max)) end
end
end
-- Register
minetest.register_node(stomped_node_name, stomped_node_desc)
poschangelib.register_stomp(node_name, stomped_node_name, stomp_desc)
-- Stomp to itself to reset the timer on restomp
poschangelib.register_stomp(stomped_node_name, stomped_node_name, stomp_desc)
return stomped_node_name
end
--- Register a stomped node that has a chance to be transformed from the source.
-- @param source_node_name The name of the node before it is stomped
-- @param stomp chance Inverted chance that the source node is stomped on walking.
-- One of X.
-- @param stomp_node_name The name of the node after it is stomped
function poschangelib.register_stomp(source_node_name, stomp_node_name, stomp_desc)
if type(stomp_node_name) == 'function' and not stomp_desc.name then
minetest.log('error', 'No stomp name given with a function for ' .. source_node_name)
return
end
if type(source_node_name) == 'table' then
for i, node_name in ipairs(source_node_name) do
poschangelib.register_stomp(node_name, stomp_node_name, stomp_desc)
end
return
end
if not stomps[source_node_name] then
stomps[source_node_name] = {}
end
stomp_desc.dest_node_name = stomp_node_name
stomp_desc_defaults(source_node_name, stomp_desc)
-- Insert in stomps
local inserted = false
local i = 1
-- insert while keeping ascending priority order
while i < #stomps[source_node_name] and not inserted do
if stomps[source_node_name][i].priority > stomp_desc.priority then
table.insert(stomps[source_node_name], i, stomp_desc)
inserted = true
end
i = i + 1
end
-- not inserted: there is no other stomp for this node, insert it.
if not inserted then table.insert(stomps[source_node_name], stomp_desc) end
poschangelib.add_player_walk_listener(stomp_desc.name, walk_listener, {source_node_name})
end
minetest.register_chatcommand('stomp', {
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player then return false, 'Player not found' end
if not minetest.check_player_privs(player, {server=true}) then return false, 'Stomp requires server privileges' end
local pos = player:getpos()
local node_pos = {['x'] = pos.x, ['y'] = pos.y - 1, ['z'] = pos.z}
local node = minetest.get_node(node_pos)
if not stomps[node.name] or #stomps[node.name] == 0 then
return false, 'No stomping data found for ' .. node.name
elseif #stomps[node.name] > 1 then
local local_stomps = stomps[node.name]
minetest.chat_send_player(name, 'Multiple stomping data found for ' .. node.name)
minetest.chat_send_player(name, 'Use /stomp X to choose which one to trigger.')
for i, v in ipairs(local_stomps) do
minetest.chat_send_player(name, ' ' .. i .. ') ' .. local_stomps[i].name)
end
return false
else
poschangelib.do_stomp(node_pos, node, stomps[node.name][1])
return true
end
end,
})