ID:2379353
 
(See the best response by Lummox JR.)
Code:
var/Regenloop=0
mob
proc
Regen()
set background=1
sleep(30)
for(var/mob/M in TotalPlayers) //All players currently online
if(Regenloop==1)
goto skip
if(M.hit) //If M was hit(In combat)
Regenloop=1
sleep(200)
Regenloop=0
goto skip
else
Regenloop=1
M.health+=M.maxhealth/100*M.basehealthregen
if(M.health>M.maxhealth)
M.health=M.maxhealth
M.UpdateHMB() //This updates the Healthbar
M.MenuUpdate() //This updates the statpanel
Regenloop=0
skip
Regen()


Problem description:
I've been trying out different things for some passive health Regeneration. Basically my question is, if there is a better way of doing this?
Absolutely, for one, you have a mob proc that loops over every mob in the world every 3 ticks, that's not gonna go over so well.

Secondly you're using goto in a very inappropriate way.

As a mob proc, you don't really need the for(), since it's not in the global scope. You don't need 'goto' to make the proc loop, you can use while() for that.
mob
proc
Regen()
set waitfor = 0
while(src)
sleep(30)
// Do stuff here.

Login()
..()
Regen()


Now Regen() will call every 3 ticks for every mob that logs in.

If you wanted something that only ran the loop while they were damaged you could make the condition of the while() based on their health, which would terminate the loop when that condition was met, then you could call the Regen() proc when they took damage (and the loop wasn't already running).
Thank you very much! This works flawlessly. Reduced the CPU usage by quite alot as well
Best response
An even bigger issue in your original code, besides the inappropriate goto, was the recursion:

proc/Regen()
...
Regen()

When the proc calls itself, it's expecting a return value, so it ends up on the stack. Eventually you'll have this giant stack of Regen() calls that are expecting a response, and the whole thing comes crashing down. If you're going to do a sleeping loop like that, there are only two ways:

proc/Regen()
spawn() // let caller keep going after this
while(condition)
...
sleep(timeout)

Or:

proc/Regen()
...
spawn(timeout) Regen()

The first one leaves the proc running but allows it to sleep, and it doesn't call itself because it can simply loop. The early spawn() statement is so that control is passed back to the caller. The second version just calls itself with spawn(), which performance-wise shouldn't be any different from the first, although there are some advantages to the first.
I don't know about anyone else, but I call all my loops from mob/Stat() proc

it loops every tick and if you want to slow it down if you can use a time variable.

Ex:

mob
var/tmp/loop_delay
Stat()
if(loop_delay-world.realtime<=0) //Checks to see if world.realtime is greater than loop_delay. You could also do if(loop_delay<=world.realtime) for more simple operations
LoopMe();
LoopMe2();
AnotherLoop();
loop_delay=world.realtime+50 //Add's 5 seconds until the next loop.
..()
<dm>
In response to LemonYourAid
Stat() isn't called every tick. It's called every few ticks and whenever a statpanel is clicked, and its next call can be delayed by sleeping.

If you want a loop that runs once every tick (according to world.tick_lag, which is also the rate that world.time is updated), then you need what Lummox shows above with a while() and a timeout equal to world.tick_lag.

Also, world.realtime is very inaccurate and not practical for short-term scheduling. Last I checked, it can take about a minute, maybe more, before it updates its value. Instead, use world.time, which updates according to (and is as precise as) world.tick_lag.
Oh wow, you're totally right.



I must've been crazy thinking it matched tick_lag. To think, I thought this since the FPS variable was introduced. Maybe it was just an illusion the whole time.