diff --git a/docs/superpowers/specs/2026-03-11-daytime-nap-design.md b/docs/superpowers/specs/2026-03-11-daytime-nap-design.md index 8ce7644..313c8c8 100644 --- a/docs/superpowers/specs/2026-03-11-daytime-nap-design.md +++ b/docs/superpowers/specs/2026-03-11-daytime-nap-design.md @@ -11,28 +11,32 @@ When sleeping during daytime, treat it as a **nap**: calculate just enough energ ## Nap Target Calculation ``` +cycleTicks = Math.round((100 / ENERGY_DECAY_PER_TICK) * (1 + 1 / DAY_NIGHT_RATIO)) remainingDayTicks = (SLEEP_NIGHT_START - gameTime) * cycleTicks energyNeeded = remainingDayTicks * ENERGY_DECAY_PER_TICK * constitutionMultiplier -napTarget = ENERGY_THRESHOLD + energyNeeded + NAP_BUFFER +napTarget = clamp(ENERGY_THRESHOLD + energyNeeded + NAP_BUFFER, 0, 100) ``` -- `NAP_BUFFER`: ~15 energy. NPCs arrive at nightfall around 35 energy — above the 20 emergency threshold, below the 60 voluntary night sleep threshold. -- `constitutionMultiplier`: `1 - (con - 10) * 0.03` (same formula used in needsDecaySystem). +- `cycleTicks`: derived from runtime constants (`ENERGY_DECAY_PER_TICK`, `DAY_NIGHT_RATIO`), both available via `rc` in npcBrainSystem. +- `NAP_BUFFER`: 15 energy. NPCs arrive at nightfall around 35 energy — above the 20 emergency threshold, below the 60 voluntary night sleep threshold. +- `constitutionMultiplier`: `1 - (con - 10) * 0.03` (same formula used in needsDecaySystem). Uses point-in-time CON; transient stat modifiers may shift this slightly, which is acceptable. +- `energyNeeded` represents the **post-wake awake energy budget** — how much the NPC will burn while awake between nap and nightfall. +- Note: for early-morning crashes (gameTime ~0.1-0.3), napTarget approaches 85-100 and the nap is effectively identical to full sleep. The nap diverges meaningfully for afternoon crashes. ## Wake Logic In the sleep wake check (npcBrainSystem.ts): -1. Determine if this is a **daytime sleep** (gameTime < SLEEP_NIGHT_START). -2. Calculate nap target. -3. **Guard**: if napTarget >= SLEEP_VOLUNTARY_ENERGY_THRESHOLD (60), treat as normal full sleep (too close to nightfall for a nap to matter — avoid wake-then-immediately-re-sleep). -4. **Daytime nap**: wake immediately when energy >= napTarget (clean cutoff, no probabilistic ramp). +1. Check if it is currently daytime (gameTime < SLEEP_NIGHT_START). This check happens every tick during sleep, not just at entry. +2. If daytime: calculate nap target. +3. **Guard**: if napTarget < SLEEP_VOLUNTARY_ENERGY_THRESHOLD (60), the NPC is so close to nightfall that a nap would leave them with too little energy — they'd wake and immediately re-trigger voluntary night sleep. Skip the nap; use normal full sleep instead. +4. **Daytime nap**: if napTarget >= 60, wake immediately when energy >= napTarget (clean cutoff, no probabilistic ramp). 5. **Night or guarded**: use existing probabilistic wake (85-100 range). ## Edge Cases - **Night arrives while napping**: switch to normal night sleep wake logic (85-100 probabilistic). The NPC was exhausted enough to sleep into night — that's fine. -- **Close to nightfall**: napTarget would be low (~25-30), which is below 60, so the guard triggers normal full sleep. No silly wake-then-re-sleep. +- **Close to nightfall**: napTarget would be low (~25-30), which is below the 60 guard threshold, so it falls through to normal full sleep. No silly wake-then-re-sleep. - **Constitution variation**: high-CON NPCs decay slower, so shorter naps. Low-CON NPCs need more banked energy, so longer naps. Natural. ## Recovery Rate @@ -49,6 +53,14 @@ Same as normal sleep (0.048/tick). No change. A nap is just a shorter sleep. - `shared/src/constants.ts` — add NAP_BUFFER - `server/src/systems/npcBrainSystem.ts` — modify sleep wake logic +## Tests + +- Nap target calculation at various times of day (morning, midday, afternoon) +- Wake behavior: immediate wake at nap target (no probabilistic ramp) +- Guard condition: napTarget < 60 triggers full sleep instead of nap +- Edge case: night arrives during nap → switches to normal wake logic +- Edge case: napTarget clamped to 100 for very early crashes + ## What Stays the Same - Sleep entry conditions (emergency < 20, voluntary night < 60)