Player Death / Defeat
The key words MUST, MUST NOT, MAY, and REQUIRED below are used as in RFC 2119.
Defeat is the failure exit of a field session. It is the mirror of the success exit (clear the objectives, ride the end telepipe home): the player goes down, and the run ends with a cost instead of a reward. There is no permadeath — defeat is a setback, not a game-over.
Trigger
- The player enters Defeat the moment their HP reaches 0 while in a field (a quest field or a free-roam field). HP only ever reaches 0 in the field — the city has no combat — so Defeat is field-only.
- The death knockdown itself (the lying-down animation) is the
DOWNbranch of the Player State contract; this page governs what happens around that animation.
Damage immunity during the defeat window
- From the moment the player is defeated — HP reaches 0
and
diedis emitted — through the return-to-city transition completing, the player MUST be damage-immune. This covers the entire window: the knockdown, the red overlay and "You were defeated" prompt, the moment Yes is pressed, and every frame of the scene transition until the city has loaded a fresh player. - While immune,
take_damage()MUST be a no-op: no HP or PP change MUST occur, no hit reaction or knockdown MUST play, anddiedMUST NOT be emitted again. The player's hurtbox MUST NOT register further hits from any source (enemy melee, projectiles, techniques). - Immunity MUST NOT be coupled to current HP. The defeat transaction revives the player to full HP synchronously when Yes is pressed, before the scene swaps — so an HP-based guard alone would stop firing while the still-live field player remains hittable for the transition frames. Immunity MUST persist across that full-HP revive.
The defeat screen
- On defeat a translucent red overlay MUST cover the play area. It MUST render below the HUD elements — the HP/PP panel and other HUD stay visible on top of the red layer.
- Gameplay input MUST be blocked while the defeat screen is up (the player cannot move, attack, or open menus).
- A PSZ-style text box MUST appear reading "You were defeated", followed by the question "Would you like to return to the city?" with two options, Yes and No.
- Choosing No MUST return to the "You were defeated" prompt. There is no other way to dismiss the screen — the only exit is Yes.
- Choosing Yes MUST return the player to the city, arriving at the same spot a telepipe arrival uses (the counter's telepipe-arrival point), without consuming a Telepipe item or spawning a placed telepipe in either the field or the city.
Consequences of returning (the "Yes" path)
- Session ends. Returning to the city via defeat ends the
field session exactly like the
return_to_cityexit — the expedition is over and is NOT resumable. To retry a quest the player MUST re-accept it at the guild counter. Any active telepipe is cleared (a session end cancels it). - Meseta penalty. The player MUST lose 50% of the meseta they are carrying, rounded down (e.g. 101 → lose 50, keep 51). Meseta in storage (the bank) MUST NOT be touched.
- Revive. On arrival in the city the player MUST be revived — HP restored to full. They do not walk around the city at 0 HP.
- No items are lost beyond the meseta penalty. Quest-item progress is moot because the session has ended.
Persistence
The defeat screen is transient UI and MUST NOT persist. The only durable effects of a defeat are the meseta deduction and the session ending; both are in-memory game state, saved with the normal save flow.
Conformance scenario
- Accept a quest and enter its field carrying 100 meseta (with, say, 500 in storage).
- Take lethal damage so HP hits 0 → the avatar drops into the knockdown loop, the red overlay fades in below the HUD, and the "You were defeated" prompt appears asking to return to the city.
- Choose No → the prompt stays up (no exit, no movement).
- Choose Yes → 50 meseta is deducted (carried 100 → 50; storage stays 500), the player is revived at full HP, and arrives in the city at the telepipe-arrival spot. No telepipe was used or spawned.
- The quest session is over: the guild counter offers the quest fresh again (re-accept to retry), not a resume.
Implemented by
scripts/3d/player/player.gd(Player) — emitsdiedwhentake_damage()drives HP to 0; the field controller listens for it. The_is_defeatedflag is latched on that death branch and gates the top oftake_damage()to a no-op for the whole defeat window, enforcing the damage-immunity contract above (HP-decoupled, survives the full-HP revive).scripts/3d/ui/defeat_screen.gd(DefeatScreen) — the red overlay + "You were defeated" prompt (Yes/No), mounted below the HUD. On Yes it runs the defeat transaction and routes to the city counter via the telepipe-arrival spawn variant.scripts/autoloads/session_manager.gd(SessionManager) —defeat_return_to_city()applies the consequences (50% carried meseta penalty, full-HP revive,return_to_citysession end) as one transaction and returns{ meseta_lost }. The penalty and revive are written to the active character dict (the persistent source of truth) as well as theGameStatemirror — the city re-syncsGameStatefrom the character on entry (CharacterManager._sync_to_game_state), so a mirror-only change would be clobbered on arrival. Carried meseta lives on the character;stored_meseta(the bank) is never touched.