ID:1865655
 
(See the best response by Kaiochao.)
Code:
mob/proc/Hunger()
set background = 1
spawn while(src)
sleep(125)
if(src.Hunger != 0 && src.Hunger != 100)
src.Vitals(5, "Hunger", "Subtraction")
switch(src.Hunger)

if(0)
if(src.Miscellaneous["Hunger"] != -0.15)
if(src.Miscellaneous["Hunger"] < 0)
var/Effect/Starvation/S = locate() in src.Effects
if(S) S.Deactivate(src)
else if(src.Miscellaneous["Hunger"] > 0)
var/Effect/Indigestion/I = locate() in src.Effects
if(I) I.Deactivate(src)
src.Miscellaneous["Hunger"] = -0.15
new /Effect/Starvation (src, src)

if(1 to 14)
if(src.Miscellaneous["Hunger"] != -0.1)
if(src.Miscellaneous["Hunger"] < 0)
var/Effect/Starvation/S = locate() in src.Effects
if(S) S.Deactivate(src)
else if(src.Miscellaneous["Hunger"] > 0)
var/Effect/Indigestion/I = locate() in src.Effects
if(I) I.Deactivate(src)
src.Miscellaneous["Hunger"] = -0.1
new /Effect/Starvation (src, src)

if(15 to 29)
if(src.Miscellaneous["Hunger"] != -0.05)
if(src.Miscellaneous["Hunger"] < 0)
var/Effect/Starvation/S = locate() in src.Effects
if(S) S.Deactivate(src)
else if(src.Miscellaneous["Hunger"] > 0)
var/Effect/Indigestion/I = locate() in src.Effects
if(I) I.Deactivate(src)
src.Miscellaneous["Hunger"] = -0.05
new /Effect/Starvation (src, src)

if(30 to 44)
if(src.Miscellaneous["Hunger"] != -0.03)
if(src.Miscellaneous["Hunger"] < 0)
var/Effect/Starvation/S = locate() in src.Effects
if(S) S.Deactivate(src)
else if(src.Miscellaneous["Hunger"] > 0)
var/Effect/Indigestion/I = locate() in src.Effects
if(I) I.Deactivate(src)
src.Miscellaneous["Hunger"] = -0.03
new /Effect/Starvation (src, src)

if(45 to 55)
if(src.Miscellaneous["Hunger"] != 0)
if(src.Miscellaneous["Hunger"] < 0)
var/Effect/Starvation/S = locate() in src.Effects
if(S) S.Deactivate(src)
else if(src.Miscellaneous["Hunger"] > 0)
var/Effect/Indigestion/I = locate() in src.Effects
if(I) I.Deactivate(src)
src.Miscellaneous["Hunger"] = 0

if(56 to 70)
if(src.Miscellaneous["Hunger"] != 0.1)
if(src.Miscellaneous["Hunger"] < 0)
var/Effect/Starvation/S = locate() in src.Effects
if(S) S.Deactivate(src)
else if(src.Miscellaneous["Hunger"] > 0)
var/Effect/Indigestion/I = locate() in src.Effects
if(I) I.Deactivate(src)
src.Miscellaneous["Hunger"] = 0.1
new /Effect/Indigestion (src, src)

if(71 to 85)
if(src.Miscellaneous["Hunger"] != 0.2)
if(src.Miscellaneous["Hunger"] < 0)
var/Effect/Starvation/S = locate() in src.Effects
if(S) S.Deactivate(src)
else if(src.Miscellaneous["Hunger"] > 0)
var/Effect/Indigestion/I = locate() in src.Effects
if(I) I.Deactivate(src)
src.Miscellaneous["Hunger"] = 0.2
new /Effect/Indigestion (src, src)

if(86 to 99)
if(src.Miscellaneous["Hunger"] != 0.3)
if(src.Miscellaneous["Hunger"] < 0)
var/Effect/Starvation/S = locate() in src.Effects
if(S) S.Deactivate(src)
else if(src.Miscellaneous["Hunger"] > 0)
var/Effect/Indigestion/I = locate() in src.Effects
if(I) I.Deactivate(src)
src.Miscellaneous["Hunger"] = 0.3
new /Effect/Indigestion (src, src)

if(100)
if(src.Miscellaneous["Hunger"] != 0.5)
if(src.Miscellaneous["Hunger"] < 0)
var/Effect/Starvation/S = locate() in src.Effects
if(S) S.Deactivate(src)
else if(src.Miscellaneous["Hunger"] > 0)
var/Effect/Indigestion/I = locate() in src.Effects
if(I) I.Deactivate(src)
src.Miscellaneous["Hunger"] = 0.5
new /Effect/Indigestion (src, src)

Effect
var
Name
Aura
Type
Colour
Active = 0
Potency = 0
Duration = 30
Stats[0]
Focus[0]
Effects[0]

Starvation
Name = "Starvation"
Aura = "Leaking Aura"
Type = "Negative"
Colour = "Sky Blue"
Potency = 0
Duration = 0
Stats = list("Vitality", "Vitality", "Tenacity", "Dexterity")
Focus = list("Health", "Stamina", "All", "All")
Effects = list("Hunger")

Activate(var/mob/M)
..()
if(src.Active == 1)
src.Active = 2
M.Status["Tenacity"] -= round(M.Tenacity * (Potency / 100))
M.Status["Dexterity"] -= round(M.Dexterity * (Potency / 100))
var/HLH = (M.Max_Health / 10) * (Potency / 100)
var/STM = (M.Max_Stamina / 10) * (Potency / 100)
M.Interaction = "Health: [HLH] | Stamina: [STM]"
M.Vitals(STM, "Stamina", "Subtraction")
M.Vitals(HLH, "Health", "Subtraction")
M.DeathCheck()

Deactivate(var/mob/M)
M.Status["Tenacity"] += round(M.Tenacity * (Potency / 100))
M.Status["Dexterity"] += round(M.Dexterity * (Potency / 100))
return ..()

Indigestion
Name = "Indigestion"
Aura = "Leaking Aura"
Type = "Negative"
Colour = "Brown"
Potency = 0
Duration = 0
Stats = list("Celerity", "Dexterity")
Focus = list("All", "All")
Effects = list("Hunger")

Activate(var/mob/M)
..()
if(src.Active == 1)
src.Active = 2
M.Status["Celerity"] -= round(M.Celerity * (Potency / 100))
M.Status["Dexterity"] -= round(M.Dexterity * (Potency / 100))

Deactivate(var/mob/M)
M.Status["Celerity"] += round(M.Celerity * (Potency / 100))
M.Status["Dexterity"] += round(M.Dexterity * (Potency / 100))
return ..()

New(mob/User, mob/Target)
if(src.Effects.Find("Hunger") && src.Potency == 0)
src.Potency = abs(Target.Miscellaneous["Hunger"]) * 100
Target.Effects.Add(src)
src.Ticker(User, Target)

proc
Ticker(var/mob/User, var/mob/Target)
Target.UpdateHUD(src, "Effect")
Target.Effect(src)

Activate(var/mob/M)
if(src.Active == 0)
src.Active = 1

Deactivate(var/mob/M)
src.Active = 0
M.Effects.Remove(src)
M.UpdateHUD(src, "Effect")
del src

mob/proc
Effect(var/Effect/E)
if(E.Duration > 0 && E.Duration)
for(var/Count = 1; Count <= E.Duration; Count++)
sleep(5)
E.Activate(src)
E.Deactivate(src)
else
E.Activate(src)
while(E.Active > 0)
sleep(5)
E.Activate(src)
E.Deactivate(src)


Explainations:
"Hunger()"
The first function is a constant loop that is called upon the creation of a new mob. As you can hopefully hypothesize, it's purpose is to slowly decrease over the mob's hunger over time, and gives them certain effects based on their level of starvation / indigestion.

"Effects"
Effects, a new datum I created, is simply used to create effects attached to mobs in a Object-Oriented way. Currently, there's a lot more Effects than just Starvation and Indigestion, however, the unique problem I'm having is involving the Hunger System I'm trying to set-up so I'm only showing those two for now.

"Effect()"
The mob's Effects function, which is to keep looping over the details of the intended Effect until either:

1) That Effect's Duration comes to an end
2) The Effect is no longer active,
3) Or the mob's death.

In the case of Starvation, it should be a case of the mob either dying or the Effect no longer being Active; while in the case of Indigestion, it is only until the Effect is no longer Active.

Problem Description:
In theory, in an alternate version of testing, the system works without incident and as intended, prior to actually using the Effect() with an Effect on a mob. The problem arises in the use of the sleep statement actually causing other functions to be halted -- Namely, the Hunger().

So, I tried replacing the sleep statement within the Effect() with spawn statement instead, which actually causes Dream Seeker to crash completely when it's time to update the mob's Hunger().

In another alternative, I tried switching the sleep statement within the Hunger() to spawn statement (the sleep statement in the Effects() was a spawn statement at the time), which caused extreme CPU latency (easily over 100%), and still caused it to crash when it was time to update the mob's Hunger.

So, is there anyone who can identify what is that I'm possibly doing wrong here or provide me with a feasible alternative?
You can either call Effect() with spawn:
spawn Target.Effect(src)

Or you can use the undocumented waitfor setting, which means delays/sleeps in Effect() will not block other procs:
mob/proc
Effect(var/Effect/E)
set waitfor = FALSE
// etc.

In general, you can't replace sleep with spawn because they aren't the same kind of thing, since sleep is an instruction like new or del that just works in one line, and spawn is a control statement like if, for, while, etc., meaning it affects a block of code (e.g. indented section) after it.
The suggestions you provided work well enough, and thanks for that, though I'm not sure I understand why it worked (the "spawn Target.Effect(src)" suggestion, that is).

I'm aware of how spawn requires indentation to work, since it usually complains for syntax upon compilation before allowing you to run it; but I don't think I understand why it worked in this way as oppose to using "spawn (5) E.Activate(src)" within the Effects().

Also, there's a small thing I forgot to mention that's been occurring as well. When the sleep statement is reached; that is, every half-a-second, the mob which is currently within the Effects() loop flickers for some reason. Any idea why that might be?
In response to Yoren
Best response
spawn has an alternate form that doesn't require a number. In that case, the delay defaults to 0.

There doesn't appear to be any appearance changes in the code you've given, so it's probably in one of the procs being called that aren't shown, like UpdateHUD().
Well, I get that, but then why could I not have simply used "spawn (5) E.Activate(src)" ? Or rather, why would that line cause it to crash?

Also, the UpdateHud() just makes an icon of the Effect on the screen if the mob is a Player. Furthermore, this flicker happens in sync with the sleep(5) statement, whereas UpdateHud() is called only when activating the Ticker, and Deactivating the Effect, so that shouldn't have anything to do with it.

mob/proc
UpdateHUD(var/A, var/Type)
switch(Type)
if("Skill")
if(src.Mode == "Player")
var/Skill/S = A
if(S)
if(src.Effects.Find(S))
var/obj/HUD/Effects/H = locate() in src.client.screen
if(H)
var/image/I = image('Skills.dmi', S.Name)
I.name = S.Name
I.pixel_x += src.Effects.len * 20
I.pixel_y -= 20
H.overlays += I
else
var/obj/HUD/Effects/H = locate() in src.client.screen
if(H)
for(var/image/I in H.overlays)
if(I.icon_state == S.Name)
del I
break

if("Effect")
if(src.Mode == "Player")
var/Effect/E = A
if(E)
if(src.Effects.Find(E))
var/obj/HUD/Effects/H = locate() in src.client.screen
if(H)
var/image/I = image('Effects.dmi', E.Name)
I.name = E.Name
I.pixel_x += src.Effects.len * 20
I.pixel_y -= 20
H.overlays += I
else
var/obj/HUD/Effects/H = locate() in src.client.screen
if(H)
for(var/image/I in H.overlays)
if(I.icon_state == E.Name)
del I
break
im no expert here so maybe im wrong in saying this but constant loops really urk me. Like i never use them. I'd devise another method that would judge time, without actually counting down time and waiting in a infinite loop.

Games have done this with several different methods. Fast traveling, when you wait(skyrim/fallout). Yea fallout may have done it your way, but it was also single player. Correct me if im wrong kaiochao but wouldnt that mean every player is loading this infinite loop? would that not cause a problem lag wise?(assuming you didnt have a great shell) But at the same time if it only happens every 12.5 seconds its probably not bad. i just am ocd about infinite loops
In response to DanteVFenris
Having tested it myself, the loop seem to have very little overhead, and my CPU Usage barely ever goes over 2%.

If I'm also right in how Dream Maker works, the loop being ran for players will generate overhead only on the player's end, and not the server.

On the other hand, I've already tested it with numerous AIs and again, the CPU Usage barely ever goes over 2%.

Overall as well, the loop picks one case-statement of the switch-statement, and then it's iteration is pretty much over within a maximum of 6 lines from there (with a minimum of 0 lines after it picks a case-statement). And that's assuming that it passes the first if-statement, to see if a mob's Hunger has reached any caps.

All in all, I think it's pretty efficient.

Also, I do not understand your references of / connections to Skyrim / Fallout's Fast-Travel Systems....

Those type of systems don't have anything in common with a Hunger System, to the best of my knowledge.
If I'm also right in how Dream Maker works, the loop being ran for players will generate overhead only on the player's end, and not the server.

Completely wrong. If it's code written in DM it always runs on the server, and it only runs on one thread.
In response to Ter13
Oh, well. That's good to make note of.

Erh - Just so we're not entirely off track though, I'm still trying to figure out the issue with the mob's flickering?
In response to Yoren
fallout new vegas had a hardcore mode or something like that. Put it on and you required to drink and eat. So when you waited or fast traveled the bars would go down. But like i said it also did it over time.
In response to DanteVFenris
Ohhh, now I understand what you meant, okay.
In response to Yoren
Yoren wrote:
Oh, well. That's good to make note of.

Erh - Just so we're not entirely off track though, I'm still trying to figure out the issue with the mob's flickering?

I don't think there's anything in this that would cause the mobs to flicker.

At least, not in the code you are showing.
In response to Ter13
Yeah, that's why I'm baffled by it too....

Well, I don't know if it'll help, but I'll also show the code for the Vitals(), as well as New() for mobs (since that's where I first call Hunger()).

mob/proc/Vitals(var/Value, var/Vital, var/Operation)
if(Value > 0)
var/Result = 0
var/Percent = 0
switch(Vital)
if("Health")
Result += src.StatusCheck(list("Health"))
switch(Operation)
if("Addition")
Result = src.Cur_Health + Value
if(Result > src.Max_Health)
Result = src.Max_Health
if("Subtraction")
Result = src.Cur_Health - Value
if(Result < 0)
Result = 0
src.Cur_Health = round(Result)
Percent = round((src.Cur_Health / src.Max_Health) * 100)

if("Stamina")
Result += src.StatusCheck(list("Stamina"))
switch(Operation)
if("Addition")
Result = src.Cur_Stamina + Value
if(Result > src.Max_Stamina)
Result = src.Max_Stamina
if("Subtraction")
Result = src.Cur_Stamina - Value
if(Result < 0)
Result = 0
src.Cur_Stamina = round(Result)
Percent = round((src.Cur_Stamina / src.Max_Stamina) * 100)

if("Bones")
Result += src.StatusCheck(list("Bones"))
switch(Operation)
if("Addition")
Result = src.Bones + Value
if(Result > 100)
Result = 100
if("Subtraction")
Result = src.Bones - Value
if(Result < 0)
Result = 0
src.Bones = round(Result)
Percent = (src.Bones + src.Blood + src.Organs) / 3

if("Blood")
Result += src.StatusCheck(list("Blood"))
switch(Operation)
if("Addition")
Result = src.Blood + Value
if(Result > 100)
Result = 100
if("Subtraction")
Result = src.Blood - Value
if(Result < 0)
Result = 0
src.Blood = round(Result)
Percent = (src.Bones + src.Blood + src.Organs) / 3

if("Organs")
Result += src.StatusCheck(list("Organs"))
switch(Operation)
if("Addition")
Result = src.Organs + Value
if(Result > 100)
Result = 100
if("Subtraction")
Result = src.Organs - Value
if(Result < 0)
Result = 0
src.Organs = round(Result)
Percent = (src.Bones + src.Blood + src.Organs) / 3

if("Hunger")
Result += src.StatusCheck(list("Hunger"))
switch(Operation)
if("Addition")
Result = src.Hunger + Value
if(Result > 100)
Result = 100
if("Subtraction")
Result = src.Hunger - Value
if(Result < 0)
Result = 9
src.Hunger = round(Result)
Percent = src.Hunger

if(src.Mode == "Player")
Percent = round(Percent, 10)
if(Percent < 10)
for(var/obj/HUD/Vitals/H in src.client.screen)
if(H.Type == Vital)
H.overlays.Cut()
return

var/State = Vital
if(State == "Organs" || State == "Blood" || State == "Bones")
State = "Wounds"
for(var/obj/HUD/Vitals/H in src.client.screen)
if(H.Type == State)
if(State == "Wounds")
if(src.Bones < 41 || src.Blood < 71 || src.Organs < 51)
H.overlays += image('Heads-Up Display.dmi', "[State] | Death")
else if(src.Bones < 61 || src.Blood < 81 || src.Organs < 71)
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | Danger")
else if(src.Bones < 81 || src.Blood < 91 || src.Organs < 91)
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | Caution")
else
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | Good")
else
if(H.tag == "[Vital] L")
if(Percent < 21)
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | [Percent]%")
else
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | 20%")
else if(H.tag == "[Vital] ML")
if(Percent < 30)
H.overlays.Cut()
else if(Percent > 29 && Percent < 41)
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | [Percent]%")
else
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | 40%")
else if(H.tag == "[Vital] M")
if(Percent < 50)
H.overlays.Cut()
else if(Percent > 49 && Percent < 61)
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | [Percent]%")
else
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | 60%")
else if(H.tag == "[Vital] MR")
if(Percent < 70)
H.overlays.Cut()
else if(Percent > 69 && Percent < 81)
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | [Percent]%")
else
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | 80%")
else if(H.tag == "[Vital] R")
if(Percent < 90)
H.overlays.Cut()
else
H.overlays.Cut()
H.overlays += image('Heads-Up Display.dmi', "[State] | [Percent]%")

mob/New(loc)
..(loc)
if(!src.Job)
src.Job = "None"
if(!src.Class)
src.Class = "None"
if(src.Mode == "Player")
light = new /light/day_night(src, 4)
var/image/I = image('Faces.dmi', "No Face")
src.Face = I
src.Miscellaneous["Movement"] = 1
src.Hunger()


On the "off-chance" (very likely) that this code doesn't give any type of insight into why this flickering is occurring, is it possibly related to something outside of Dream Maker? Like, hardware related? (I am afterall running on a pretty dated computer that using a Athlon Dual Core Processor; 2.0GHz, and 4GB of Memory, so it might just appear this way on my own machine?).
In response to Ter13
Hah! Nevermind. I fixed the flickering problem. I had completely forgot that I had left in that unfinished DeathCheck() that has in a few icon related stuff, heh.

Derp~

Nevertheless, thanks for everyone's help and input. Cheers, mates.
for(var/obj/HUD/Vitals/H in src.client.screen)

Protip: You should never do this. You should be storing your objects that you need to update later in variables, not looping through containers that hold unrelated stuff.
I suppose that makes sense as it would generate unnecessary processing....

I'll remember to tweak that, thanks.
In response to Yoren
Sorry, I forgot to address your issue with the spawn vs sleep IN the loop.

while(TRUE)
spawn(5)
DoThing()

// vs

spawn while(TRUE)
sleep 5
DoThing()

The first loop has no delay between iterations. On the first iteration, it starts a background timer that calls DoThing() in 0.5 seconds. Then, without delay, it moves on to the next iteration, starting another background timer, then continues to do this as many times as it can without letting the global clock move on to the next tick, causing game freezing.

The second loop is spawned such that the loop itself doesn't block other procs, but the loop actually is delayed inside.
So basically,

while(TRUE)
spawn(5)
DoThing()


Is the same as:

while(TRUE)
sleep(5)
DoThing()


?

That is, if I'm understanding what you're saying correctly.
The sheer amount of if/else you're using there is dizzying. That has to be hard to maintain. I strongly suggest finding a way to whittle it down.

Also, I'm seeing a lot of code like this:
if("Addition")
Result = src.Organs + Value
if(Result > 100)
Result = 100
if("Subtraction")
Result = src.Organs - Value
if(Result < 0)
Result = 0

You could clean up your code a great deal by using min() and max().

if("Addition")
Result = min(100, src.Organs + Value)
if("Subtraction")
Result = max(0, src.Organs - Value)

Better still, in case someone slips a negative Value by you, would be this:

if("Addition")
Result = max(0, min(100, src.Organs + Value))
if("Subtraction")
Result = max(0, min(100, src.Organs - Value))

...or you can simplify things by saying Value=max(Value,0) at the beginning of the whole routine, as a sanity check.
The only thing that could be done about the if/else statements is using a switch-statement instead, but in terms of "looks" -- It's fine to me, so I've no problem with that.

As for the one-liners for ensuring that no values are passed below / above any of the caps, I'll definitely see to those.

EDIT: The if/else statements would be changed anyway when I get around to putting vitals into variables for updating
Page: 1 2