-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinit.lua
More file actions
332 lines (267 loc) · 8.94 KB
/
Copy pathinit.lua
File metadata and controls
332 lines (267 loc) · 8.94 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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
--------------------------------------------------------
-- Minetest :: Axon Event Propagation Mod (axon)
--
-- See README.txt for licensing and release notes.
-- Copyright (c) 2019-2020, Leslie E. Krause
--
-- ./games/minetest_game/mods/axon/init.lua
--------------------------------------------------------
--local S1,S1_ = Stopwatch( "axon" )
axon = { }
local sources = { }
local stack_size = 0
--------------------
local random = math.random
local pow = math.pow
local ceil = math.ceil
local min = math.min
local function check_limits( v, min_v, max_v )
return v >= min_v and v <= max_v
end
local function clamp( val, min, max )
return val < min and min or val > max and max or val
end
local function get_signal_strength( max_value, cur_value, slope )
return pow( cur_value / max_value, 1 - clamp( slope, 0, 1 ) )
end
local function punch_object( obj, groups, pos )
stack_size = stack_size + 1
assert( stack_size < 10, "punch_object( ): Aborting callback relay due to possible recursion" )
obj:punch( obj, 1.0, {
full_punch_interval = 1.0,
damage_groups = groups
}, pos and vector.direction( obj:get_pos( ), pos ) or nil )
stack_size = stack_size - 1
end
--------------------
local function ContactStimulus( node_name, group, intensity, chance, period, power )
local self = { }
self.group = group
self.propagator = "contact"
self.period = period
self.on_action = function ( source_pos, target_obj )
if random( chance ) == 1 then
local touch_counts = minetest.count_nodes_in_area(
vector.add( source_pos, -0.5 ), vector.add( source_pos, 0.5 ), { node_name }, true )
local count = touch_counts[ node_name ]
if count > 0 then
local damage = intensity * pow( count, power )
target_obj:punch( target_obj, period, {
full_punch_interval = period,
damage_groups = { [group] = damage },
}, nil )
end
end
end
return self
end
local function ImmersionStimulus( node_name, group, intensity, chance, period )
local self = { }
self.group = group
self.method = "immersion"
self.period = period
self.on_action = function ( source_pos, target_obj )
if random( chance ) == 1 then
local node = minetest.get_node( vector.round( source_pos ) )
local node_group = string.match( node_name, "^group:(.+)" )
if node.name == node_name or node_group and minetest.get_item_group( node.name, node_group ) > 0 then
target_obj:punch( target_obj, period, {
full_punch_interval = period,
damage_groups = { [group] = intensity },
}, nil )
end
end
end
return self
end
local function RadiationStimulus( node_name, group, intensity, chance, period, radius, power )
local self = { }
self.group = group
self.propagator = "radiation"
self.period = period
self.on_action = function ( source_pos, target_obj )
if random( chance ) == 1 then
local touch_counts = minetest.count_nodes_in_area(
vector.add( source_pos, -radius ), vector.add( source_pos, radius ), { node_name }, true )
local count = touch_counts[ node_name ]
if count > 0 then
local damage = intensity * pow( count, power )
target_obj:punch( target_obj, period, {
full_punch_interval = period,
damage_groups = { [group] = damage },
}, nil )
end
end
end
return self
end
local function EmissionStimulus( node_name, group, intensity, chance, period, radius, slope )
local self = { }
self.group = group
self.propagator = "radiation"
self.period = period
self.on_action = function ( source_pos, target_obj )
if random( chance ) == 1 then
-- get nearest node position in a radius, including the center
local node_pos = minetest.find_node_near( source_pos, radius, { node_name }, true )
if node_pos then
local length = min( radius, vector.distance( node_pos, source_pos ) )
local damage = ceil( intensity * get_signal_strength( radius, radius - length, slope ) )
target_obj:punch( target_obj, period, {
full_punch_interval = period,
damage_groups = { [group] = damage },
}, nil )
end
end
end
return self
end
local function AxonPropagator( obj )
local event_defs = { }
local clock = 0.0
local delay = 0.0
local self = { }
self.start = function ( stimulus )
table.insert( event_defs, {
group = stimulus.group,
period = stimulus.period,
expiry = clock + stimulus.period,
started = clock,
on_action = stimulus.on_action
} )
end
self.on_step = function ( dtime, pos )
clock = clock + dtime
for i, v in ipairs( event_defs ) do
if clock >= v.expiry and clock > v.started then
v.expiry = clock + v.period
v.on_action( pos, obj )
end
end
end
return self
end
function AxonObject( self, armor_groups )
local old_on_step = self.on_step
local old_on_punch = self.on_punch
local propagator = AxonPropagator( self.object )
if not self.receptrons then
self.receptrons = { }
end
-- initialize armor groups
if not armor_groups then
armor_groups = { }
end
for k, v in pairs( self.receptrons ) do
armor_groups[ k ] = v.sensitivity or 0
end
self.object:set_armor_groups( armor_groups )
-- start stimulus propagators
for k, v in pairs( sources ) do
for idx, stimulus in ipairs( v ) do
-- ignore stimulii without a receptron
if self.receptrons[ stimulus.group ] then
propagator.start( stimulus )
end
end
end
self.on_step = function ( self, dtime, pos, ... )
--S1()
propagator.on_step( dtime, pos )
--S1_()
old_on_step( self, dtime, pos, ... )
end
self.generate_direct_stimulus = function ( self, obj, groups )
if obj ~= self.object then
punch_object( obj, groups )
end
end
self.generate_radial_stimulus = function ( self, radius, speed, slope, groups, classes )
local pos = self.object:get_pos( )
for obj in mobs.iterate_registry( pos, radius, radius, classes ) do
local length = vector.distance( pos, obj:get_pos( ) )
if obj ~= self.object and length <= radius then
local damage_groups = { }
for k, v in pairs( groups ) do
damage_groups[ k ] = v * get_signal_strength( radius, radius - length, slope )
end
if speed > 0 then
minetest.after( length / speed, function ( )
punch_object( obj, damage_groups )
end )
else
punch_object( obj, damage_groups )
end
end
end
end
self.on_punch = function ( self, puncher, time_from_last_punch, tool_capabilities, direction, damage )
if puncher == self.object then
-- filter and relay the incoming stimulus
for k, v in pairs( tool_capabilities.damage_groups ) do
local receptron = self.receptrons[ k ]
if receptron and check_limits( v, receptron.min_intensity or 1, receptron.max_intensity or 65536 ) then
-- if receptron returns false, then stimulus was handled
if not receptron.on_reaction( self, v, direction ) then return true end
end
end
end
-- if stimulus wasn't handled by a receptron, fallback to on_punch method
return old_on_punch( self, puncher, time_from_last_punch, tool_capabilities, direction, damage )
end
end
--------------------
axon.register_source = function ( node_name, stimulus_list )
if not sources[ node_name ] then
sources[ node_name ] = { }
end
for i, v in pairs( stimulus_list ) do
local stimulus
if v.propagator == "contact" then
stimulus = ContactStimulus( node_name, v.group, v.intensity, v.chance, v.period, v.power )
elseif v.propagator == "immersion" then
stimulus = ImmersionStimulus( node_name, v.group, v.intensity, v.chance, v.period )
elseif v.propagator == "radiation" then
stimulus = RadiationStimulus( node_name, v.group, v.intensity, v.chance, v.period, v.radius, v.power )
elseif v.propagator == "emission" then
stimulus = EmissionStimulus( node_name, v.group, v.intensity, v.chance, v.period, v.radius, v.slope )
else
error( "axon.register_source(): Unknown propagator specified in stimulus property table" )
end
table.insert( sources[ node_name ], stimulus )
end
end
axon.register_source_group = function ( node_group, node_names )
for i, v in ipairs( node_names ) do
local groups = minetest.registered_nodes[ v ].groups
groups[ node_group ] = 1
minetest.override_item( v, { groups = groups } )
end
end
axon.generate_direct_stimulus = function ( obj, groups )
punch_object( obj, groups )
end
axon.generate_radial_stimulus = function ( pos, radius, speed, slope, groups, classes )
for obj in mobs.iterate_registry( pos, radius, radius, classes ) do
local length = vector.distance( pos, obj:get_pos( ) )
if length <= radius then
local damage_groups = { }
for k, v in pairs( groups ) do
damage_groups[ k ] = ceil( v * get_signal_strength( radius, radius - length, slope ) )
end
if speed > 0 then
minetest.after( length / speed, function ( )
punch_object( obj, damage_groups, pos )
end )
else
punch_object( obj, damage_groups, pos )
end
end
end
end
--------------------
dofile( minetest.get_modpath( "axon" ) .. "/sources.lua" )
-- compatibility for Minetest S3 engine
if not minetest.count_nodes_in_area then
dofile( minetest.get_modpath( "axon" ) .. "/compatibility.lua" )
end