PlayerState
Everything the player *is*, as plain data: one table plus the rules that govern it. No entities, no input, no engine — so the survival numbers can be reasoned about on their own, and the interface reads the same values the simulation runs on rather than a copy that drifts.
game.systems.PlayerState
Everything the player is, as plain data: one table plus the rules that govern it. No entities, no input, no engine — so the survival numbers can be reasoned about on their own, and the interface reads the same values the simulation runs on rather than a copy that drifts.
local PlayerState = require("game.systems.PlayerState")
local state = PlayerState.new()
What is in it
new()— health, stamina, torch and battery, what is carried, clues, the objective index, the power, and whether the run has ended.- Movement —
spendStamina,canStartSprint. - The torch —
useTorch,torchLow,replaceBattery. - Carrying —
has,give(a unique item already taken is not taken again, which is what stops two peers pocketing the same key),addClue,clueCount,requiredCluesFound. - Damage —
damage(returns true when it is fatal),heal. - The objective chain —
objectiveMet(state, step)says what each step waits for, andsyncObjectivewalks the chain forward as far as the player has actually got. It only ever moves on. ending(state)— which ending the run earned, from the clues found and whether the secret room was opened.
The co-op split
toShared and applyShared state, once, what the whole session agrees on —
a door, a key, an objective, the power — against what is only yours: your
stamina, your health, your torch. Mixing the two is how a co-op game
desynchronises, so the split is a function rather than a judgement at each
call site.
Scoped to this part · feeds back into the world's score.