103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
...
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
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
...
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
...
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
|
elseif d.pos then -- find effects anchored here and people in range
for id,effect in pairs(starlit.effect.active) do
if not effect.anchor then goto skip end -- this intentionally excludes attached effects
if ineffectrange(effect,d.pos,d.range) then
effects[#effects+1] = {v=effect,i=id}
end
::skip::end
local ppl = minetest.get_objects_inside_radius(d.pos,d.range)
if #targets == 0 then targets = ppl else
for _,p in pairs(ppl) do targets[#targets+1] = p end
end
end
-- iterate over targets to remove from any effect's influence
for _,t in pairs(targets) do
................................................................................
if v == effect then starlit.effect.active[k] = nil break end
end
end
end
end
starlit.effect.ensorcelled = function(player,effect)
if type(player) == 'string' then player = minetest.get_player_by_name(player) end
for _,s in pairs(starlit.effect.active) do
if effect and (s.name ~= effect) then goto skip end
for _,sub in pairs(s.subjects) do
if sub.player == player then return s end
end
::skip::end
return false
................................................................................
end
end
until idx >= #starlit.effect.active
end
end
-- when a new effect is created, we analyze it and make the appropriate calls
-- to minetest.after to queue up the events. each job returned needs to be
-- saved in 'jobs' so they can be canceled if the effect is disjoined. no polling
-- necessary :D
starlit.effect.cast = function(proto)
local s = table.copy(proto)
s.jobs = s.jobs or {} s.vfx = s.vfx or {} s.sfx = s.sfx or {}
s.impacts = s.impacts or {} s.subjects = s.subjects or {}
s.delay = s.delay or 0
s.visual = function(subj, def)
s.vfx[#s.vfx + 1] = {
handle = minetest.add_particlespawner(def);
subject = subj;
}
end
s.visual_caster = function(def) -- convenience function
local d = table.copy(def)
d.attached = s.caster
s.visual(nil, d)
................................................................................
s.impacts[#s.impacts+1] = rec
etbl[#etbl+1] = rec
end
return etbl
end
s.abort = function()
for _,j in ipairs(s.jobs) do j:cancel() end
for _,v in ipairs(s.vfx) do minetest.delete_particlespawner(v.handle) end
for _,i in ipairs(s.sfx) do s.silence(i) end
for _,i in ipairs(s.impacts) do i.effect:stop() end
end
s.release_subject = function(si)
local t = s.subjects[si]
for _,f in pairs(s.sfx) do if f.subject == t then s.silence(f) end end
for _,f in pairs(s.impacts) do if f.subject == t then f.effect:stop() end end
for _,f in pairs(s.vfx) do
if f.subject == t then minetest.delete_particlespawner(f.handle) end
end
s.subjects[si] = nil
end
local interpret_timespec = function(when)
if when == nil then return 0 end
local t if type(when) == 'number' then
t = s.duration * when
................................................................................
end
if t then return math.min(s.duration,math.max(0,t)) end
log.err('invalid timespec ' .. dump(when))
return 0
end
s.queue = function(when,fn)
local elapsed = s.starttime and minetest.get_server_uptime() - s.starttime or 0
local timepast = interpret_timespec(when)
if not timepast then timepast = 0 end
local timeleft = s.duration - timepast
local howlong = (s.delay + timepast) - elapsed
if howlong < 0 then
log.err('cannot time-travel! queue() called with `when` specifying timepoint that has already passed')
howlong = 0
end
s.jobs[#s.jobs+1] = minetest.after(howlong, function()
-- this is somewhat awkward. since we're using a non-polling approach, we
-- need to find a way to account for a caster or subject walking into an
-- existing antimagic field, or someone with an existing antimagic aura
-- walking into range of the anchor. so every time a effect effect would
-- take place, we first check to see if it's in range of something nasty
if not s.disjunction and -- avoid self-disjunction
((s.caster and starlit.effect.probe(s.caster:get_pos()).disjunction) or
................................................................................
for _,sub in pairs(s.subjects) do addobj(sub.player,sub) end
elseif spec.where == 'pos' then specs[#specs+1] = { spec = {pos = s.anchor} }
else specs[#specs+1] = { spec = {pos = spec.where} } end
for _,sp in pairs(specs) do
sp.spec.gain = sp.spec.gain or spec.gain
local so = {
handle = minetest.sound_play(spec.sound, sp.spec, spec.ephemeral);
ctl = spec;
-- object = sp.obj;
subject = sp.subject;
}
stbl[#stbl+1] = so
s.sfx[#s.sfx+1] = so
end
................................................................................
for _,snd in pairs(snds) do s.silence(snd) end
end)
end
end)
end
s.silence = function(sound)
if not sound.handle then return end
if sound.ctl.fade == 0 then minetest.sound_stop(sound.handle)
else minetest.sound_fade(sound.handle,sound.ctl.fade or 1,0) end
end
local startqueued, termqueued = false, false
local myid = #starlit.effect.active+1
s.cancel = function()
s.abort()
starlit.effect.active[myid] = nil
end
................................................................................
iteration = iteration;
iterationcount = itercount;
timeleft = timeleft;
timeelapsed = s.duration - timeleft;
lastreturn = lastreturn;
}
if nr ~= false and iteration < itercount then
s.jobs[#s.jobs+1] = minetest.after(int.period,
function() iterate(nr) end)
end
end
if int.after
then s.queue(int.after, iterate)
else s.queue({whence=0, secs=s.period}, iterate)
end
................................................................................
end
end
if s.sounds then
for when,what in pairs(s.sounds) do s.play(when,what) end
end
starlit.effect.active[myid] = s
if not termqueued then
s.jobs[#s.jobs+1] = minetest.after(s.delay + s.duration, function()
if s.terminate then s:terminate() end
starlit.effect.active[myid] = nil
end)
end
s.starttime = minetest.get_server_uptime()
return s
end
minetest.register_on_dieplayer(function(player)
starlit.effect.disjoin{target=player}
end)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
...
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
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
...
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
...
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
|
elseif d.pos then -- find effects anchored here and people in range
for id,effect in pairs(starlit.effect.active) do
if not effect.anchor then goto skip end -- this intentionally excludes attached effects
if ineffectrange(effect,d.pos,d.range) then
effects[#effects+1] = {v=effect,i=id}
end
::skip::end
local ppl = core.get_objects_inside_radius(d.pos,d.range)
if #targets == 0 then targets = ppl else
for _,p in pairs(ppl) do targets[#targets+1] = p end
end
end
-- iterate over targets to remove from any effect's influence
for _,t in pairs(targets) do
................................................................................
if v == effect then starlit.effect.active[k] = nil break end
end
end
end
end
starlit.effect.ensorcelled = function(player,effect)
if type(player) == 'string' then player = core.get_player_by_name(player) end
for _,s in pairs(starlit.effect.active) do
if effect and (s.name ~= effect) then goto skip end
for _,sub in pairs(s.subjects) do
if sub.player == player then return s end
end
::skip::end
return false
................................................................................
end
end
until idx >= #starlit.effect.active
end
end
-- when a new effect is created, we analyze it and make the appropriate calls
-- to core.after to queue up the events. each job returned needs to be
-- saved in 'jobs' so they can be canceled if the effect is disjoined. no polling
-- necessary :D
starlit.effect.cast = function(proto)
local s = table.copy(proto)
s.jobs = s.jobs or {} s.vfx = s.vfx or {} s.sfx = s.sfx or {}
s.impacts = s.impacts or {} s.subjects = s.subjects or {}
s.delay = s.delay or 0
s.visual = function(subj, def)
s.vfx[#s.vfx + 1] = {
handle = core.add_particlespawner(def);
subject = subj;
}
end
s.visual_caster = function(def) -- convenience function
local d = table.copy(def)
d.attached = s.caster
s.visual(nil, d)
................................................................................
s.impacts[#s.impacts+1] = rec
etbl[#etbl+1] = rec
end
return etbl
end
s.abort = function()
for _,j in ipairs(s.jobs) do j:cancel() end
for _,v in ipairs(s.vfx) do core.delete_particlespawner(v.handle) end
for _,i in ipairs(s.sfx) do s.silence(i) end
for _,i in ipairs(s.impacts) do i.effect:stop() end
end
s.release_subject = function(si)
local t = s.subjects[si]
for _,f in pairs(s.sfx) do if f.subject == t then s.silence(f) end end
for _,f in pairs(s.impacts) do if f.subject == t then f.effect:stop() end end
for _,f in pairs(s.vfx) do
if f.subject == t then core.delete_particlespawner(f.handle) end
end
s.subjects[si] = nil
end
local interpret_timespec = function(when)
if when == nil then return 0 end
local t if type(when) == 'number' then
t = s.duration * when
................................................................................
end
if t then return math.min(s.duration,math.max(0,t)) end
log.err('invalid timespec ' .. dump(when))
return 0
end
s.queue = function(when,fn)
local elapsed = s.starttime and core.get_server_uptime() - s.starttime or 0
local timepast = interpret_timespec(when)
if not timepast then timepast = 0 end
local timeleft = s.duration - timepast
local howlong = (s.delay + timepast) - elapsed
if howlong < 0 then
log.err('cannot time-travel! queue() called with `when` specifying timepoint that has already passed')
howlong = 0
end
s.jobs[#s.jobs+1] = core.after(howlong, function()
-- this is somewhat awkward. since we're using a non-polling approach, we
-- need to find a way to account for a caster or subject walking into an
-- existing antimagic field, or someone with an existing antimagic aura
-- walking into range of the anchor. so every time a effect effect would
-- take place, we first check to see if it's in range of something nasty
if not s.disjunction and -- avoid self-disjunction
((s.caster and starlit.effect.probe(s.caster:get_pos()).disjunction) or
................................................................................
for _,sub in pairs(s.subjects) do addobj(sub.player,sub) end
elseif spec.where == 'pos' then specs[#specs+1] = { spec = {pos = s.anchor} }
else specs[#specs+1] = { spec = {pos = spec.where} } end
for _,sp in pairs(specs) do
sp.spec.gain = sp.spec.gain or spec.gain
local so = {
handle = core.sound_play(spec.sound, sp.spec, spec.ephemeral);
ctl = spec;
-- object = sp.obj;
subject = sp.subject;
}
stbl[#stbl+1] = so
s.sfx[#s.sfx+1] = so
end
................................................................................
for _,snd in pairs(snds) do s.silence(snd) end
end)
end
end)
end
s.silence = function(sound)
if not sound.handle then return end
if sound.ctl.fade == 0 then core.sound_stop(sound.handle)
else core.sound_fade(sound.handle,sound.ctl.fade or 1,0) end
end
local startqueued, termqueued = false, false
local myid = #starlit.effect.active+1
s.cancel = function()
s.abort()
starlit.effect.active[myid] = nil
end
................................................................................
iteration = iteration;
iterationcount = itercount;
timeleft = timeleft;
timeelapsed = s.duration - timeleft;
lastreturn = lastreturn;
}
if nr ~= false and iteration < itercount then
s.jobs[#s.jobs+1] = core.after(int.period,
function() iterate(nr) end)
end
end
if int.after
then s.queue(int.after, iterate)
else s.queue({whence=0, secs=s.period}, iterate)
end
................................................................................
end
end
if s.sounds then
for when,what in pairs(s.sounds) do s.play(when,what) end
end
starlit.effect.active[myid] = s
if not termqueued then
s.jobs[#s.jobs+1] = core.after(s.delay + s.duration, function()
if s.terminate then s:terminate() end
starlit.effect.active[myid] = nil
end)
end
s.starttime = core.get_server_uptime()
return s
end
core.register_on_dieplayer(function(player)
starlit.effect.disjoin{target=player}
end)
|