Add stomping.

This commit is contained in:
Karamel 2017-08-22 19:33:18 +02:00
parent 27461f01d2
commit 6bafb69d49
4 changed files with 300 additions and 0 deletions

@ -17,6 +17,8 @@ Summary
- Watch players' movements
- Watch players walking on particular nodes
- Add _on_walk on nodes
- Set stomping on nodes
- Add footprints
- Configuration/Performances tweaking
@ -172,6 +174,72 @@ by hand.
Set stomping on nodes
---------------------
Stomping is a dedicated subset of walk listeners that allows to replace a node by an
other when a player walks on it.
It is required to be able to declare multiple outputs without messing with one
another. And just for ease of use.
Stomping are registered with poschangelib.register_stomp. It takes 3 parameters:
- source_node_name: the name of the node that can be stomped. It can be a table
with multiple node names to declare the same stomping behaviour to multiple
nodes at once.
- stomp_node_name: the name of the replacement node, or a function.
- stomp_desc: stomping parameters.
The stomp description is a table that can contains the following set of keys:
- chance: inverted chance that the stomp occurs (default 1)
- duration: time in second after which the stomp reverts.
When not set, the stomp is forever. If set it will override duration_min and
duration_max.
- duration_min: same as duration but to add some randomness for each node.
- duration_max: same as duration but to add some randomness for each node.
- priority: the priority rank. The lower, the more important it is (default 100)
- name: name that is used as walk listener name.
Default is <source>__to__<stomp> and is rather indigest but probably unique.
It has no default when using a function in stomp_node_name and must be set.
- source_node: set it if you want the stomp to revert to an other node than the
original.
When multiple stompings are registered for the same node, only the first
triggered is applied. This is when priority comes into play. When a player walks
on a node that can be stomped, a roll is made for each stomp in order of
priority (the lowest priority first). If the roll succeeds, the node is replaced
and the next stomps are not run.
When using a function instead of a stomp node name, this function is a regular
player walk listener. It must return a node name or nil. If it returns nil, the
stomp is not done and the priority check is not stopped (see just below).
Add footprints
--------------
Use poschangelib.register_footprint to quickly register footprinted nodes and
the stomping associated to it. The function takes 2 parameters:
- node_name: the name of the node to extend.
- stomp_desc: see above.
The stomp description can have dedicated keys and values:
- footprint_texture: set it to use an other texture than the one embedded.
A new node will be registered with most of it's description copied from the
original node. It's top texture will have the footprint layer on it and the
stomping behaviour will be automatically created.
poschangelib.register_footprint returns the footprinted node name.
Configuration/Performances tweaking
-----------------------------------

@ -336,6 +336,8 @@ local function check_on_walk_triggers(player, old_pos, pos, trigger_meta)
trigger_on_walk(player, pos_below, node_below, node_def, trigger_meta)
end
dofile(minetest.get_modpath(minetest.get_current_modname()) .. '/stomping.lua')
--[[
-- Main loop
--]]

230
stomping.lua Normal file

@ -0,0 +1,230 @@
--[[
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,
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B