Needs and Physical State¶
The ambient systems that run whether or not the player is doing anything.
Sources: plugins/hunger/, plugins/stamina/, plugins/flashlight/,
metatables/sv_player.lua, libraries/sv_player.lua (the tick), sh_config.lua.
1. The tick¶
There is exactly one timer driving everything (libraries/sv_player.lua):
every 0.1 s → PlayerTenthSecond(player) for every player
every 1.0 s → PlayerSecond(player) for every player
+ unconditional auto-CSVar broadcast
Two rates, both per-player, both fired as hooks so plugins can attach. Simple and effective. The auto-CSVar broadcast riding along on the 1 s tick is D-11.
Design note: two fixed rates for all ambient simulation is a good default and worth keeping. The new implementation should make the rate a property of the need, since temperature and disease do not need 1 Hz.
2. Hunger¶
plugins/hunger/. The only need in the original with real consequences.
| Setting | Default | Meaning |
|---|---|---|
Hunger Minutes |
30 | Minutes from empty to full hunger |
Hunger Damage |
5 | Damage per second while starving |
Hunger Death |
true | Starvation can kill |
Semantics — note the polarity¶
_Hunger.amount runs 0 → 100 where 100 is starving. Eating reduces it. The HUD bar
therefore fills as you get hungrier. Worth flagging because every instinct says otherwise,
and the reimplementation should almost certainly invert it to a satiation value.
Per second: amount += 1 / (HungerMinutes × 0.6), clamped to [0, 100].
Damage¶
At amount == 100:
- Knocked out → the ragdoll takes the damage, not the player.
- In a vehicle → double damage. The source comment reads: "cars are dicks".
- Otherwise → normal damage.
The vehicle penalty is deliberate anti-AFK design: you cannot park up and idle safely.
Feeding¶
items/base/food.lua: onUse refuses if amount < 25 — "You do not need any more
food!" — otherwise reduces hunger by the item's Hunger value. You cannot stockpile
satiation, so food must be found repeatedly rather than eaten in bulk once.
Suicide and respawn¶
Deliberately anti-exploit, and the whole reason the plugin tracks lastTeam and suicided:
if (suicided OR hunger was 100) AND team unchanged:
respawn with hunger = 50 and health = 50
else: respawn with hunger = 0
You cannot clear starvation by killing yourself — you come back half-starved and at half health. Changing team does reset it, which is a deliberate loophole: taking a new job is a fresh start. Excellent, cheap, targeted design. Preserve the behaviour exactly.
Bar suppression¶
If the player is dead, arrested or tied, hunger is reported to the client as 0 — hiding the bar rather than adding a "hide" flag. A UI concern solved by lying in the data layer; preserve the intent (no hunger bar in jail), not the method.
3. Stamina¶
plugins/stamina/. Runs on the 0.1 s tick.
| Setting | Default |
|---|---|
Stamina Drain |
0.35 per 0.1 s while sprinting |
Stamina Restore |
0.15 per 0.1 s otherwise |
| Jump cost | 5 flat |
Drain is ~2.3× restore, so sprinting is a resource with a real recovery cost.
Injury coupling¶
Below 50 health, both rates worsen proportionally:
drain += (50 - health) × 0.05 -- at 1 hp: +2.45, i.e. 8× the base rate
restore -= (50 - health) × 0.0025 -- at 1 hp: 0.0275, i.e. 5× slower
A wounded player cannot run and cannot recover. There is no separate "injury" system in the original — this is it, and it is entirely emergent from two lines of arithmetic. Elegant, and a good model for the new medical system: injury should degrade capability continuously, not toggle a state.
Speed scaling¶
Movement speed is interpolated by remaining stamina, not gated:
runSpeed = lerp(WalkSpeed → RunSpeed, stamina/100) -- 150 → 275
walkSpeed = lerp(IncapacitatedSpeed → WalkSpeed, stamina/100) -- 100 → 150
You slow down smoothly as you tire. No cliff edge.
Exhaustion, with hysteresis¶
- Stamina ≤ 1 →
Incapacitate()and setNWBool "Exausted"(sic — the typo is in the source and in the networked variable name). - While exhausted, recovery to 50 does nothing — the state persists.
- Above 50 → exhaustion clears and speeds resume.
A 1 / 50 hysteresis band: collapsing is instant, recovering takes you to half stamina. Deliberate — the comment reads "If you get exausted, it takes a while to wear off. ;)"
The plugin also implements PlayerCanBeRecapacitated to return false while exhausted, so
nothing else in the game can stand you up early. This is the cleanest example in the
codebase of a plugin extending a core mechanic through a declared gate rather than patching
it — the model for the new event bus.
Suppression¶
Stamina does not run while arrested, tied, holding an entity, or in noclip.
4. Flashlight¶
plugins/flashlight/ — battery-limited, drains with use, drawn on the shared
DrawBottomBars hook. Turned off automatically by Arrest and TieUp. A small system, but
it establishes the pattern: a resource attached to a tool, with a HUD bar.
5. Physical states¶
Six distinct states, all built on the Incapacitate primitive. Full details in
06_CRIME_AND_POLICE.md; summarised here because
they belong to the character state model.
| State | Set by | Speed | Weapons | Escapable |
|---|---|---|---|---|
| Incapacitated | primitive | 100 / 100, no jump | — | via Recapacitate (gated) |
| Exhausted | stamina ≤ 1 | incapacitated | — | at stamina > 50 |
| Knocked out | KnockOut(30 s) |
ragdolled | — | player input after the timer |
| Tied | TieUp (5 s) |
incapacitated | stored, returned | 1 struggle, 30 s meter |
| Arrested | Arrest(300 s) |
incapacitated | confiscated, items lost | timer only |
| Sleeping | /sleep, 5 s still |
— | — | player input |
_Sleeping deserves a mention: a player who stands still for Sleep Waiting Time
(5 s) can deliberately fall asleep — and sleeping regenerates health at 1 HP every
2 seconds (libraries/sv_player.lua, in the 1 s branch of the tick; the two-second rate is
a workaround because "the game doesn't like fractions"). The ragdoll's health is kept in
sync as it heals.
So resting is the game's only passive healing, and it costs you your ability to react. That
is a real mechanic dressed as an affordance, and it should be preserved as one — it gives
/sleep a reason to exist beyond flavour, and it means a wounded player has to choose
between recovering and staying alert.
Other work on the same tick¶
Three more behaviours ride the 1 s branch and belong in the character state model:
- Stuck detection — sets
_StuckInWorldif the player is notIsInWorld()or a trace straight up hits sky (i.e. they are behind the world). - Idle kick —
Autokick timedefaults to 15 minutes of inactivity. _HideHealthEffectsclears automatically above 50 HP — the "paracetamol" screen effect suppression expires on its own.
6. What does not exist¶
Against the master plan's Phase 10:
| Need | Legacy status |
|---|---|
| Hunger | ✅ Full implementation |
| Stamina | ✅ Full implementation (not in the master plan's list, but present) |
| Thirst | ❌ Absent — despite a 9-item drinks category. Drinks derive from food and restore hunger. |
| Sleep | ⚠️ No sleep need, but sleeping is the only passive healing (1 HP / 2 s) |
| Temperature | ❌ Absent |
| Injuries | ⚠️ Emergent only, via the stamina/health coupling |
| Disease | ❌ Absent |
| Bleeding | ⚠️ Exists as meta:Bleed, but it is cosmetic (decals) and its timer is buggy |
| Healing | ⚠️ Consumable items only (drugs category) |
| Movement penalties | ✅ Stamina-scaled speed |
| Visual effects | ⚠️ Blood decals; _HideHealthEffects |
| Audio effects | ❌ Absent |
So: one real need, one excellent hidden one, and everything else is new work.
The important lesson is not the gap list but how the two implemented needs are built:
- A single value on a fixed tick.
- Consequences that scale continuously rather than switching.
- Hysteresis on any state that is unpleasant to enter.
- An anti-exploit rule at the respawn boundary.
- A declared gate so other systems can veto recovery.
- A HUD bar through one shared drawing hook.
That is the specification for the generic needs framework. Thirst, temperature and disease should be configurations of it, not new code.
7. What survives¶
Survives: the two-rate tick; hunger's rate, damage and vehicle penalty; the refuse-to-eat
threshold; the suicide/respawn anti-exploit including the change-team loophole; stamina's
drain/restore asymmetry; injury coupling to both rates; smooth speed interpolation; the
1/50 exhaustion hysteresis; PlayerCanBeRecapacitated as a gate; needs suppressed while
arrested or tied; the shared bottom-bar HUD; sleeping as the only passive healing; idle
kick and stuck-in-world detection.
Does not survive: hunger's inverted polarity; needs living in plugin-private
ply._Xxx tables; needs never being persisted (hunger resets on every join); zeroing a value
to hide its UI; the Exausted misspelling on the wire; the bleed timer bug.