Health and armor rewards are additive grants the recipient applies to itself
Status: Accepted (2026-06-24)
Context
A health or armor pickup, or any other reward, must raise a player's vital by some amount. The player's client applies it to the game's live value, while damage the client has taken but not reported yet may be in flight; the server keeps its own ledger of each life's health and armor (ADR 0045), which trails the client by the network delay. The question is what the server sends: an amount to add, or a target value. This extends the grant primitive of ADR 0013 and uses the server facade of ADR 0009.
Decision
- A reward is an additive grant with a server-chosen ceiling.
GiveHealth/GiveArmorcarryamountandmax. The recipient setsclamp(current + amount, current, max)against its live value: it adds, clamps tomax, and never lowers the vital (a value already abovemaxgains nothing). The server adds to its ledger the same way when sending. The packet goes on the reliable event lane to the recipient only, which applies it to its own player; others see the new value through normal sync. - The ceiling travels on the wire. The server owns the ceiling per reward instead of a fixed 100, so a reward can go above the default maximum when the gamemode wants it.
- The facade verbs are
add_health(player, amount, max)andadd_armor(player, amount, max). In Lua:player:add_health(amount, max)/dlmp.add_health(player, amount, max), and the same for armor.amountandmaxmust be finite, positive and at most 200. - A vital grant reaches only a life the server counts. Before the first frame of a life, while the player is dead and once the server has counted it dead, the facade refuses the grant (
nil, errin Lua); the server never sends a vital grant to a dead player. Weapon grants are not affected. - The reward is not on the wire as pickup state. A client sees a pickup appear, disappear and a grant packet; what a pickup gives is decided by the gamemode on the server (ADR 0019).
- An absolute set is a different action.
set_health/set_armor(SetHealth/SetArmor) may lower the value and is not a reward.
Consequences
- Rewarding a player at or above the ceiling is harmless: the value does not change. A gamemode that wants the classic "cannot pick it up at full health" behavior declines the pickup itself, reading
player:health()/player:armor()(ADR 0019). - Adding applies cleanly on top of damage in flight; no update is lost.
Alternatives considered
- An absolute target computed on the server and pushed down. Rejected: the server's value trails the client, so a pushed target could overwrite damage or healing the client has not reported yet.
- A fixed ceiling of 100 in the client. Rejected: the ceiling is gameplay policy and belongs to the server.
- Deferring a vital grant on the client until the player is alive again. Rejected: the server counts a grant when it sends it, so a grant held back on the client would leave the server's ledger and the client apart. The server refuses the grant instead, and a dead client drops one that crosses a death in flight.
