ID:548957
 
(See the best response by Stephen001.)
Code: Hunger system
mob/var
hunger = 100

mob/proc/HungerDecrease()
if(src.hunger == 0)
src.stat1 -= 10
src.stat2 -= 10
src.statRes -= 20
src.statRest -= 20
sleep(3000)
src.hp -= 50
src << "You should eat something really fast!"
src.HungerDecrease()
else if(src.hunger >= 30 && src.hunger <= 60)
sleep(6000)
src.hunger--
src << "You are starting to starve.."
src.HungerDecrease()


Problem description: Well I haven't test that code yet, I have been studying DM language for a month, so to pratice I decided to clean and tweak the code of Rise of Heroes (No ideas to release it, my goal is to make an original game).
I have some questions, do I really need to put

src.HungerDecrease() to run the code again ?
If I set a variable to 1, and if I call it like
if(varExample)
compiler will recognized as set to 1 ?

Hope I was clear enough.
Best response
Well, I shall answer what I think is your question first.

Basically, for the compiler to brings changed variable values across from one procedure call to another (even the same procedure calling itself, as in your example), the variable needs to belong to an object or be global. In your example, hunger belongs to src (hence src.hunger to read the variable), and so yes, changes to src.hunger will be seen by the compiler after the procedure finishes.

A procedure calling itself, in the manner you have shown, is a known as recursion, and the procedure could be called a recursive procedure. Recursion has quite a few nice properties, particularly in terms of re-using code and sometimes solving a problem in a very straight-forward way. However it does have a few drawbacks.

The main drawback people find is that each time the procedure calls itself, it has to (temporarily) grab a bit of memory to record information about the procedure call it is making (local variables, who is src, who is usr etc). If the number of times your procedure calls itself is not very large, this possibly isn't a problem for you.

In your example though, the procedure calls itself on every possible route through the code. This is called infinite recursion, as the procedure can never complete, it will just keep calling itself forever. There's an issue with this. Each time the procedure calls itself, it allocates a bit of memory. That memory is freed when the procedure finishes. In your example, the procedure never finishes, and the memory is never freed. Worse still, it keeps allocating more memory (as it keeps calling itself) which cannot be freed either.

Eventually, BYOND would run out of free memory to allocate and crash. To protect itself from this, BYOND will throw a runtime error once it's reached X many deep calls. So although it may take some time, it is guaranteed that your example will throw a runtime exception, and the "hunger system" will stop working for a particular user.

A simple fix for this (although maybe not the best idea in the long term, as far as design goes) would be to do something like this:

mob/proc/HungerDecrease()
if(src.hunger == 0)
src.stat1 -= 10
src.stat2 -= 10
src.statRes -= 20
src.statRest -= 20
sleep(3000)
src.hp -= 50
src << "You should eat something really fast!"
spawn() src.HungerDecrease()
else if(src.hunger >= 30 && src.hunger <= 60)
sleep(6000)
src.hunger--
src << "You are starting to starve.."
spawn() src.HungerDecrease()


And spawn() the self-call off. The spawn()'d code will execute after the current procedure is finished, and so that little bit of memory is freed. The code still has the same behaviour to the user and use of hunger as seen before, but now that infinite recursion problem is 'fixed', as spawn() makes the self-call happen after the current call has finished.

I hope that offers some explanation, and makes sense. If it does, please +1 my post, so that other people in the future can see it was helpful. If not, please don't hesitate to ask more questions about this bit of code. Thanks.
As per the second question, the compiler does not care about the value of a variable (except in particular situations probably).
In response to Kccmt
Kccmt wrote:
As per the second question, the compiler does not care about the value of a variable (except in particular situations probably).

The compiler does care. It is it's job. If Stephen did not answer the question, then I can only assume that he meant that saying if(variablename) is the same as saying if(variablename == 1) or if(variablename == TRUE). And it is.
If you just type if(variablename), and variablename's value is 1 or greater, or if it is a string (or basically has any value assigned to it at all with the exception of integers 0 and below), then it will execute the code under that if() statement.
Stephen001@ Thank you for your explanation, lemme see if I understood. Whenever I wanna call a function to run again, I shall use spawn() ? But wait a second, you said it's a solution but not the best right ? Should I know anything else about it ?

Albro1@ So if I do
var/variableName = 1 // So here I am setting it to true, right ?

if(variableName) // Compiler recognizes as true, right ?
etc...


Am I right ?
Well, the spawn() thing is for functions that call themselves, yep. What you can do instead though, and it perhaps reads a little better, is re-structure the function, to remove the self-call:

mob/proc/HungerLoop()
while(TRUE)
if(src.hunger == 0)
src.stat1 -= 10
src.stat2 -= 10
src.statRes -= 20
src.statRest -= 20
sleep(3000)
src.hp -= 50
src << "You should eat something really fast!"
else if(src.hunger >= 30 && src.hunger <= 60)
sleep(6000)
src.hunger--
src << "You are starting to starve.."


One of the benefits of this is you can come along later and change the while loop to say ... remove the hunger loop entirely (the person is dead, for example), pause it, etc.
In response to Darkshini
Darkshini wrote:
Albro1@ So if I do
> var/variableName = 1 // So here I am setting it to true, right ?
>
> if(variableName) // Compiler recognizes as true, right ?
> etc...
>

Am I right ?

Yes. TRUE is >= 1, FALSE is <= 0. Vice-versa as well.
Well I did this:
mob/proc/HungerDecrease()
while(!src.dead)
if(src.hunger == 0)
src.stat1 -= 10
src.stat2 -= 10
src.statRes -= 20
src.statRest -= 20
sleep(3000)
src.hp -= 50
src << "You should eat something really fast!"
else if(src.hunger >= 30 && src.hunger <= 60)
sleep(6000)
src.hunger--
src << "You are starting to starve.."

So as long as mob isn't dead, function will run, I think I understood this concept.

For last question I guess, care to explain me what exactly does DEBUG <-- ? I have seen it in guide but didn't understand well. Thanks for the help!

EDIT:

Stephen001@ I've seen some sources calling a different function inside of another, is that correct or should I use spawn() as well ?

Albro1@ Thanks for your explanation, it really helped me.

PS: I think some people should do more tutorials, but as video and explain everything needed, as giving examples for each reference in guide. And it should be sticked, some people can learn it easily by watching a video than read a book or something.
I woul do something like the following:

mob/proc
HungerDecrease(doloop)
spawn while(doloop)
if(src.Hunger < 1)
src.stat1 = max(0, (src.stat1 - 10))
src.stat2 = max(0, (src.stat2 - 10))
src.statRes = max(0, (src.statRes - 20))
src.statRest = max(0, (src.statRest - 20))
spawn(2000) if(src.hunger < 1) {src.hp = max(0, (src.hp - 50));src<<"You should eat something really fast!"}
else if(src.Hunger < 60 && src.Hunger > 30)
src.Hunger -= rand(1, 2)
if(src.Hunger < 40 && src.Hunger > 35)src<<"You´re starting to starve!"
else
src.Hunger--;if(src.Hunger < 70){src<<"Your stomach feels a bit empty..."}
sleep(7000)
In response to Neo Rapes Everything
That's not a bad example, but I prefer do my way but I can easily understand what I wrote, but you did a good thing there and I didn't even though about it and that is max().

Good one and thanks for the example.
That way neither one of the values will reach a negative number, making it impossible to abuse in the manner of negative stats, and saves some codelines for checking if its negative, which a lot of people does as far as I´ve seen:
mob/verb/Test()
src.PlusVariable--
if(src.PlusVariable < 0){src.PlusVariable = 0}


But anyways both examples are as safe as each other.