ID:809304
 
Keywords: loop, trap
(See the best response by Albro1.)
Problem description:
I'm trying to create a loop that handles all traps in the world so that they'll all act in sync, but I'm not quite sure how to create a loop for all of them. I want each trap to also have a variable to set its delay for when it fires. Any ideas on how I'd get started creating a loop to handle traps?
trap
var/delay = 1
proc
check()
return TRUE
fire()
sleep(delay)
// do stuff
proc/trap_loop()
for(var/trap/t)
if(t.check())
t.fire()
spawn(1) trap_loop()


Like that?
In response to Albro1 (#1)
That looks a lot simpler than what I came up with!

mob/verb/startCycle()
trapCycle = new

for(var/trap/t in world)
trapCycle.traps += t

var
trapCycle/trapCycle

trapCycle
var
delay = 1

list/traps = list()

proc
loop()
for(var/trap/t in traps)
spawn t.cycle()

spawn(delay * world.tick_lag)
loop()

New()
spawn loop()

trap
parent_type = /obj

var
active = FALSE

proc
cycle()

on()

off()
/*
New()
trapCycle.traps += src
*/

/*=================================*/
spike
icon_state = "spike_off"

cycle()
if(active)
off()

else
on()

on()
flick("spike_on_anim", src)

icon_state = "spike_on"

active = TRUE

world << "on"

off()
flick("spike_off_anim", src)

icon_state = "spike_off"

active = FALSE

world << "off"


Both work fine but I'm having difficulty making the spike trap have any sort of delay without throwing off how it works. I want it to cycle between being on and off with some sort of delay but I'm not sure how to implement it using this method.
Here's a way with a slight modification of my method:
trap
var
tmp/active = FALSE
delay = 1
proc
check()
// If it's active, don't let it fire again
if(active) return FALSE
return TRUE
fire()
if(check())
activate()
activate()
active = TRUE
// Do loops or whatever you want it to do while active in here
// or make it call another proc to do it for you.
proc/trap_loop()
for(var/trap/t)
t.fire()
spawn(1) trap_loop()
Best response
Or:

trap
var
tmp/active = FALSE
tmp/cycling = FALSE
delay = 1
tmp/cycle_trap = FALSE
cycle_delay = 10
proc
check()
// If it's active, don't let it fire again
if(active) return FALSE
else if(cycling) return FALSE
else return TRUE
fire()
if(check())
if(cycle_trap) cycling = TRUE
activate()
activate()
active = TRUE
// Do loops or whatever you want it to do while active in here
// or make it call another proc to do it for you.
deactivate()
active = FALSE
if(cycle_trap)
spawn(cycle_delay) activate()
proc/trap_loop()
for(var/trap/t)
t.fire()
spawn(1) trap_loop()
That seems to be what I'm looking for. Thanks a lot Albro1!
No problem at all.
Just gonna take the opportunity to pimp a little library I made a while back that applies quite well here.

http://www.byond.com/developer/Stephen001/EventScheduling

I will make the following assumptions:

- Traps activate, performing some action, then de-activate.
- Some traps can have delays, where they active X ticks after being triggered.
- Spike traps are not like the above, they activate every X ticks, without needing a trigger.

Are these assumptions correct? If so, I'll whip you up an example.
In response to Stephen001 (#7)
Indeed they are. That would be nice of you, Stephen. :)
I knew I forgot something! ^^;

var/EventScheduler/scheduler = new()

Event/TrapEvent
var/trap/T

New(var/trap/T)
src.T = T

fire()
src.T.perform_action()

Event/TrapEvent/Cyclic
var/cycle_on

New(var/trap/T, var/cycle_on)
..(T)
src.cycle_on = cycle_on

fire()
if (src.cycle_on)
src.T.cycle_on()
else
src.T.cycle_off()


world
New()
..()
scheduler.start()

trap
parent_type = /obj

var
delay = 0
Event/TrapEvent/event = null

proc
trigger()
if (src.event == null)
src.event = new(src)
if (src.delay < 1) // immediate action
perform_action()
else if (!scheduler.is_scheduled(src.event))
scheduler.schedule(src.event, src.delay) // delayed action

perform_action()
// Whatever you'd like in here.

cyclic_trap
parent_type = /obj
on_delay = 10
off_delay = 3
var/Event/TrapEvent/Cyclic/event

New(loc)
..(loc)
event = new(src, TRUE)
src.cycle_on()

proc
cycle_on()
// perform on action, spike up etc
event.cycle_on = FALSE
scheduler.schedule(event, off_delay)

cycle_off()
// perform off action, spike down etc
event.cycle_on = TRUE
scheduler.schedule(event, on_delay)


This perhaps seems a little lengthy at first, but it offers you some nice benefits:

  • Traps are not looped over to see if they need to fire. Basically you could have 1000's of traps and the event_scheduler would only be interested in events scheduled on it. ie. traps that have been triggered by stuff.
  • Cyclic traps can be organised into different schedulers, and switched on/off uniformly. So we could say ... create an event scheduler per /area, and cyclic traps schedule on that /area scheduler. As event schedulers have procs to start(), stop() etc, you can basically "turn off" the events of all the cyclic traps within an area when no players are around, and turn them on again, simply by calling stop() on Exited() when no players are left, and start() on Entered() when the first player enters.
  • Scheduling behaviour is handled per trap type, in trigger(). So if you need special logic (calculating a delay according to player speed?), just do it in trigger(), the other traps nor the event scheduler need to care about these special cases.