class_name StatusCatalog ## Static factory registry for named statuses. ## ## Mirrors ThoughtCatalog: each factory returns a fresh Status with all fields ## set correctly. Callers must not mutate the returned object before passing ## it to Pawn.add_status() — add_status() handles severity stack-merging. ## ## Phase 9 ships: bleeding(), downed(). ## Phase 12 ships: wet(), cold(). ## Phase 17 will add: sick(), infected(). ## ## Usage pattern: ## pawn.add_status(StatusCatalog.bleeding(2)) ## ## docs/design.md "Health & status effects"; docs/design.md "Downed & death". ## ── Wet status constants (Phase 12) ───────────────────────────────────────── ## Severity labels: 1 = Damp, 2 = Soaked. const WET_DAMP_LEVEL: int = 1 const WET_SOAKED_LEVEL: int = 2 ## Per-tick accumulator rates for the 0–100 Wet scale. ## Storm doubles WET_GAIN_PER_TICK. const WET_GAIN_PER_TICK: float = 0.02 const WET_DECAY_PER_TICK: float = 0.05 ## Accumulator thresholds that flip severity. const WET_DAMP_THRESHOLD: float = 25.0 ## ≥ 25 → severity 1 (Damp) const WET_SOAKED_THRESHOLD: float = 60.0 ## ≥ 60 → severity 2 (Soaked) ## ── Cold status constants (Phase 12) ──────────────────────────────────────── ## Severity labels: 1 = Cold, 2 = Very Cold, 3 = Freezing. ## Cold activates in winter (Clock.SEASON_WINTER) or during a cold snap (any season). ## Cold snap doubles COLD_GAIN_PER_TICK. const COLD_GAIN_PER_TICK: float = 0.015 const COLD_DECAY_PER_TICK: float = 0.04 const COLD_MILD_THRESHOLD: float = 25.0 ## ≥ 25 → severity 1 const COLD_SEVERE_THRESHOLD: float = 60.0 ## ≥ 60 → severity 2 const COLD_EXTREME_THRESHOLD: float = 85.0 ## ≥ 85 → severity 3 ## Sim ticks before an untreated bleed-out causes death. ## design.md "Downed & death": 6 in-game hours. ## At 20 Hz × 60 s/min × 60 min/hr × 6 hr = 432 000 ticks at 1×. ## At Fast (5×) that compresses to ~86 400 real-Hz-ticks — but game time is ## what the player sees, so this constant is in game-time-equivalent ticks ## (the same 20-Hz tick stream; speed multiplier compresses the real clock, ## not the tick count that this timer counts against). ## Phase 20 may retune; keeping the name locked from day one per design.md. const BLEED_OUT_TICKS: int = 432000 # 6 in-game hours at 20 Hz ## HP lost per sim tick per severity level. ## Severity 1: 0.05 / tick. Severity 3: 0.15 / tick. ## At severity 3, 100 HP → 0 in 100 / 0.15 = ~667 ticks ≈ 33 sim-seconds at 1×. ## At Fast (5×) real time: ~7 s. Tune Phase 20. const BLEED_HP_PER_TICK: float = 0.05 ## Returns a Bleeding status at the given severity (1–3). ## Severity is clamped to [1, 3]. Lifetime is PERSISTENT — cleared by doctor ## treatment, not by time expiry. static func bleeding(severity: int = 1) -> Status: var s := Status.new() s.id = &"bleeding" s.kind = Status.Kind.BLEEDING s.label = "Bleeding" s.severity = clampi(severity, 1, 3) s.max_severity = 3 s.lifetime = Status.Lifetime.PERSISTENT return s ## Returns a Downed status. ## Downed has severity 1 (single-instance — you're either down or you're not). ## Cleared externally by Pawn._check_revive() when HP rises to HP_REVIVE_THRESHOLD. static func downed() -> Status: var s := Status.new() s.id = &"downed" s.kind = Status.Kind.DOWNED s.label = "Downed" s.severity = 1 s.max_severity = 1 s.lifetime = Status.Lifetime.PERSISTENT return s ## Returns a Wet status at the given severity (1 = Damp, 2 = Soaked). ## Severity is clamped to [1, WET_SOAKED_LEVEL]. Lifetime is PERSISTENT — ## cleared (or severity-adjusted) by Pawn._sync_wet_status() based on accumulator. ## The accumulator thresholds WET_DAMP_THRESHOLD / WET_SOAKED_THRESHOLD drive ## which severity the pawn is in; this factory just stamps the initial value. static func wet(severity: int = WET_DAMP_LEVEL) -> Status: var s := Status.new() s.id = &"wet" s.kind = Status.Kind.WET s.label = "Wet" s.severity = clampi(severity, WET_DAMP_LEVEL, WET_SOAKED_LEVEL) s.max_severity = WET_SOAKED_LEVEL s.lifetime = Status.Lifetime.PERSISTENT return s ## Returns a Cold status at the given severity (1 = Cold, 2 = Very Cold, 3 = Freezing). ## Severity is clamped to [1, 3]. Lifetime is PERSISTENT — ## cleared (or severity-adjusted) by Pawn._sync_cold_status() based on accumulator. static func cold(severity: int = 1) -> Status: var s := Status.new() s.id = &"cold" s.kind = Status.Kind.COLD s.label = "Cold" s.severity = clampi(severity, 1, 3) s.max_severity = 3 s.lifetime = Status.Lifetime.PERSISTENT return s