starlit  Diff

Differences From Artifact [9a580a99bf]:

To Artifact [ee92b6e11c]:


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)