-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstomping.lua
More file actions
286 lines (268 loc) · 10.3 KB
/
stomping.lua
File metadata and controls
286 lines (268 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
--[[
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
local node_mod_name = string.sub(source_node_name, 1, string.find(source_node_name, ':'))
if node_mod_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, trigger_meta)
poschangelib.chance_stomp(player, pos, node, desc, trigger_meta)
end
--- Random roll and do the stomp if it succeeds.
function poschangelib.chance_stomp(player, pos, node, node_desc, trigger_meta)
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, trigger_meta)
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.
-- @param trigger_meta Optional trigger meta, passed by walk listeners.
function poschangelib.do_stomp(player, pos, node, node_desc, stomp_desc, trigger_meta)
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 not trigger_meta then trigger_meta = {} 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
if not dest_node.name then
minetest.log('error', 'Stomping: function did not set node name for ' .. node.name)
return
end
minetest.set_node(pos, dest_node)
else
local new_node = minetest.get_node(pos)
new_node.name = stomp_desc.dest_node_name
minetest.set_node(pos, new_node)
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 while keeping param, param2 and other things
node.name = reverted_node_name
minetest.set_node(pos, node)
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)
if type(node_name) == 'table' then
-- Register all nodes from the table
local names = {}
for i, name in pairs(node_name) do
table.insert(names, poschangelib.register_footprints(name, stomp_desc))
end
return names
end
-- Single node registration
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)
-- Use a copy of stomp desc to keep it unchanged outside the function
local local_stomp_desc = table_copy(stomp_desc)
local_stomp_desc.dest_node_name = stomped_node_name
stomp_desc_defaults(node_name, local_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 local_stomp_desc.footprint_texture then
footprint_texture = local_stomp_desc.footprint_texture
end
if type(desc.tiles[1]) == 'table' then
-- Replace top texture
stomped_node_desc.tiles[1].name = desc.tiles[1].name .. '^' .. footprint_texture
else
-- Put footprints on top and keep the original texture for the rest
stomped_node_desc.tiles[1] = desc.tiles[1] .. '^' .. footprint_texture
stomped_node_desc.tiles[2] = desc.tiles[1]
end
-- Revert timer
if local_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(local_stomp_desc.duration_min, local_stomp_desc.duration_max))
end
else
stomped_node_desc.on_construct = function(pos) minetest.get_node_timer(pos):start(math.random(local_stomp_desc.duration_min, local_stomp_desc.duration_max)) end
end
end
-- Drop the original node when dug
if not desc.drop then
stomped_node_desc.drop = node_name
end
-- Register
minetest.register_node(stomped_node_name, stomped_node_desc)
poschangelib.register_stomp(node_name, stomped_node_name, local_stomp_desc)
-- Stomp to itself to reset the timer on restomp
poschangelib.register_stomp(stomped_node_name, stomped_node_name, local_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
local local_stomp_desc = table_copy(stomp_desc)
local_stomp_desc.dest_node_name = stomp_node_name
stomp_desc_defaults(source_node_name, local_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 > local_stomp_desc.priority then
table.insert(stomps[source_node_name], i, local_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], local_stomp_desc) end
poschangelib.add_player_walk_listener(local_stomp_desc.name, walk_listener, {source_node_name})
end
-- Manually trigger an stomp if it exists and if the chance test passes.
-- @return False if no stomp is registered, true otherwise.
function poschangelib.trigger_stomp(player, pos_to_stomp, chance_factor)
local node = minetest.get_node(pos_to_stomp)
local node_desc = minetest.registered_nodes[node.name]
if not node_desc or not stomps[node.name] or #stomps[node.name] == 0 then
return false
end
local stomp_desc = stomps[node.name]
if not stomp_desc then
return false
end
if chance_factor == nil then chance_factor = 1.0 end
for i, s_desc in ipairs(stomp_desc) do
if (math.random() * s_desc.chance) < (1.0 * chance_factor) then
poschangelib.do_stomp(player, pos_to_stomp, node, node_desc, s_desc)
return true
end
end
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:get_pos()
local node_pos = {['x'] = pos.x, ['y'] = pos.y - 1, ['z'] = pos.z}
local node = minetest.get_node(node_pos)
local node_desc = minetest.registered_nodes[node.name]
if not node_desc then return end -- unknown node
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 num = tonumber(param)
if num and num > 0 and num <= #stomps[node.name] then
poschangelib.do_stomp(player, node_pos, node, node_desc, stomps[node.name][num])
return true
end
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(player, node_pos, node, node_desc, stomps[node.name][1])
return true
end
end,
})