Sometimes you want the last invocation to be dominant:
mob
var/tmp
walk_loop = 0
proc
Walk(Dir,lag) //most current only
var/ctr = ++walk_loop //this will start to fail after ~16M iterations
while(walk_loop==ctr)
Step(Dir)
sleep(lag)
Sometimes you want the first invocation to be dominant:
mob
var/tmp
walk_loop = 0
proc
Walk(Dir,lag) //first only
if(walk_loop) return 0
walk_loop = 1
while(walk_loop)
Step(Dir)
sleep(lag)
walk_loop = 0
But there's a lot of problems with these approaches. Try killing a walk_loop and starting a new one during the same frame in the second example.
We could try using time too.
mob
var/tmp
walk_loop = 0
proc
Walk(Dir,lag) //most current only
var/time = walk_loop = world.time
while(walk_loop==time)
Step(Dir)
sleep(lag)
mob
var/tmp
walk_loop
proc
Walk(Dir,lag) //first only
if(!isnull(walk_loop)) return 0
var/time = walk_loop = world.time
while(walk_loop==time)
Step(Dir)
sleep(lag)
walk_loop = null
There's all kinds of problems here, because if we start and stop loops during the same frame, things get out of whack.
We need some kind of a unique value that increments per-call and isn't subject to overflow issues like the counter, and isn't subject to non-unique values EVER.
Turns out, DM provides you something that will do just the trick: args.
Every single proc gets a unique args list object. And it also forcibly destroys them the minute a proc ends, so they are reliable as you can get for checking if a proc is still running. Just store that args list somewhere outside of the proc, and you can properly gate a function to prevent double-loops:
var/list/walkers = list()
mob
var/tmp
walk_loop
proc
Walk(Dir,lag) //first only
if(!Dir)
walk_loop = null
return
if(walk_loop)
return
var/timehack = (walk_loop = args)
while(walk_loop==timehack)
//do walk loop
mob
var/tmp
walk_loop
proc
Walk(Dir,lag) //most current only
if(!Dir)
walk_loop = null
return
var/timehack = (walk_loop = args)
while(walk_loop==timehack)
//do walk loop
Using the args list to test loop continuation ensures that there will never be a duplicate entry in the list.
This is definitely weird, and probably should not be recommended, but this solution is 100% functional and beats the alternative that I wrote to handle timestamps that can never collide:
var
list/timehack_registry = list()
proc
register_timehack(id,subid,time=world.time)
var/list/l = timehack_registry[id]
if(!l)
l = list()
timehack_registry[id] = l
var/hack = l[subid]
if(istext(hack))
var/list/sl = splittext(hack,":")
if(text2num(sl[1])==time)
hack = "[time]:[text2num(sl[2])+1]"
else
hack = "[time]:0"
else if(isnull(hack))
hack = "[time]:0"
l[subid] = hack
return hack
clear_timehack(id,subid,hack)
var/list/l = timehack_registry[id]
if(!l)
return
if(istext(hack))
if(hack!=l[subid])
return
else if(!isnull(hack))
return
l -= subid
if(!l.len)
timehack_registry -= id
get_timehack(id,subid)
var/list/l = timehack_registry[id]
if(!l)
return null
return l[subid]
Yeah... Let's not do this. I have zero interest in the "correct" solution. Use the args hack. It works. It works for a reason. You don't need to overengineer everything to be logical. Don't do what I just did above. Use cached args lists as a control loop tracker.
As it turns out, the args list is a special list type. Its ID, the ID of the running proc instance it belongs to, comes from an internal counter that's incremented every time a new proc begins. That counter is four bytes so it will take a very very long time to ever loop back around. Thus the arg list is a great way of putting that counter to use.