224 lines
16 KiB
Markdown
224 lines
16 KiB
Markdown
# Death/Respawn Rules
|
||
|
||
> **Status**: In Design
|
||
> **Author**: SepComet + Claude
|
||
> **Last Updated**: 2026-04-27
|
||
> **Implements Pillar**: 操作优于数值 (Skill Over Stats), 切换即承诺 (Switch is Commitment)
|
||
|
||
## Overview
|
||
|
||
Death/Respawn Rules 是夜裔的玩家死亡与重生管理系统——定义玩家如何承受伤害、何时死亡、在何处重生、以及死亡带来的代价。它与"切换即承诺"支柱直接对齐:死亡惩罚的设计目标是让玩家在切形态时感受到风险(切错了可能死),但不会让死亡变成挫折(快速重生,代价合理)。系统采用 checkpoint 重生模型:玩家死亡后在最近的检查点重生,失去所有当前血能,当前波次重置——但不会失去关卡进度或任何永久升级。
|
||
|
||
玩家直接感受死亡时刻的**冲击**和重生时刻的**重新出发**。死亡不是"你输了"——屏幕短暂变暗、血能归零、你在检查点重新站起、敌人重新列阵。这个循环参考 Celeste 的"失败是学习"哲学——死亡足够轻,让你敢于尝试高风险操作(弹反时机、形态切换窗口);但足够重,让你在意每一次切换的决策。没有这个系统,形态切换的风险维度崩塌——玩家可以无代价地随意切换,违背"切换即承诺"支柱。
|
||
|
||
## Player Fantasy
|
||
|
||
Death/Respawn Rules 服务于一个动作游戏中最被低估的情感:**"再来一次"的渴望**。当你因切形态时机错误被 Brute 一拳砸死时,那一瞬间不是挫败——是"我知道了,下次弹反要早 0.1 秒按"。屏幕暗下、血能归零、你在检查点重新站起的 2 秒内,你已经在大脑里重放了刚才的失误并找到了修正方案。这就是好的死亡系统应该创造的感受:死亡不是终点,是**信息**。
|
||
|
||
参考 Celeste 的死亡哲学——"每次死亡都是学习,重生快到你不觉得被打断"——和 Hades 的死亡循环——"死亡是回家,不是失败"。夜裔的死亡走在两者之间:Celeste 的快速重生(检查点即重生,没有加载、没有菜单) + Hades 的资源重置(血能归零是实质性代价,迫使你重新赚取)。玩家敢于尝试高风险操作——极限弹反、狼形对撞、雾形深入包围——因为这些操作失败后的惩罚是**有限的、可量化的、可恢复的**。你不会因为一次失误而失去 30 分钟的进度,你只会失去当前的血能和当前波次——然后你立刻重新开始。
|
||
|
||
## Detailed Design
|
||
|
||
### Core Rules
|
||
|
||
**1. Player Health**
|
||
|
||
- `MaxHealth`: 100 (baseline, before any upgrades)
|
||
- `CurrentHealth`: tracked by Death/Respawn Rules, range [0, MaxHealth]
|
||
- Health displayed to player via HUD, driven by `OnPlayerHealthChanged(current, max)` event
|
||
|
||
**2. Player Damage Reception Pipeline**
|
||
|
||
```
|
||
Enemy AI produces AttackData → CombatCoordinator.ResolveFrame() → OnPlayerHit → Death/Respawn subtracts health
|
||
```
|
||
|
||
- Combat Logic's `CombatCoordinator.ResolveFrame(deltaTime)` handles all attack resolution (player + enemy) in a unified two-phase pipeline
|
||
- Enemy attacks are resolved against `PlayerState` during the damage phase (after parry nullification in the parry phase)
|
||
- Death/Respawn Rules subscribes to `CombatLogic.OnPlayerHit(PlayerDamageResult)` — subtracts FinalDamage from CurrentHealth
|
||
- No player-side resistance or armor — damage is applied as-received (mirrors Enemy AI's pure-receiver model)
|
||
|
||
**3. PlayerState Definition**
|
||
|
||
`PlayerState` is defined in Combat Logic GDD (authoritative single source). Death/Respawn reads and writes these fields:
|
||
- **Reads**: `Position`, `HitShape` (provided to CombatCoordinator for hit detection)
|
||
- **Writes**: `CurrentHealth` (updated on damage), `MaxHealth` (baseline 100, modified by upgrades), `IsDead` (set when CurrentHealth <= 0), `IsInvincible` (set during respawn i-frames)
|
||
- Other PlayerState fields (`FacingAngle`, `IsParryActive`, `ParryShape`) are managed by Form Switch SM and Combat Logic; Death/Respawn does not touch them.
|
||
|
||
**4. Death Condition**
|
||
|
||
- `CurrentHealth <= 0` → player dies
|
||
- Death is immediate on the frame health reaches 0
|
||
- Player cannot act during death (all input ignored)
|
||
- Trigger sequence: `OnPlayerDied` → systems react → respawn timer starts
|
||
|
||
**5. Checkpoint System**
|
||
|
||
- Checkpoint = start of current wave
|
||
- Updated each time `OnWaveChanged` fires (Wave Manager)
|
||
- On death: player respawns at the checkpoint wave's start state
|
||
- No manual checkpoint activation needed — automatic, invisible
|
||
|
||
### Death State
|
||
|
||
| Phase | Duration | What Happens |
|
||
|-------|----------|--------------|
|
||
| **Death Freeze** | 0.3s | Screen freeze-frame. Player input locked. `OnPlayerDied` emitted. |
|
||
| **Death Fade** | 1.0s | Screen fades to black. Audio ducks. Systems react (enemies stop, blood energy resets). |
|
||
| **Respawn Fade** | 0.5s | Player repositioned at checkpoint. HUD resets. `OnPlayerRespawned` emitted. |
|
||
| **Invincibility** | 2.0s | Player can move and act but takes no damage. Visual: player geometry pulses/flashes. |
|
||
| **Active** | — | i-frames expire. Full damage vulnerability restored. `OnPlayerVulnerable` emitted. |
|
||
|
||
Total death-to-active: ~3.8 seconds. Fast enough to feel like "instant retry."
|
||
|
||
### Respawn Flow
|
||
|
||
1. Player dies → `OnPlayerDied` emitted
|
||
2. Blood Energy Economy receives `OnPlayerDied` → resets blood energy to 0
|
||
3. Wave Manager receives `OnPlayerDied` → freezes wave state
|
||
4. Death fade completes → player position reset to checkpoint spawn point
|
||
5. Wave Manager receives `OnPlayerRespawned` → resets current wave (despawn all enemies, restart from InterWave)
|
||
6. Player enters Invincibility (2s)
|
||
7. `OnPlayerVulnerable` → player fully active, wave restarts
|
||
|
||
### Interactions with Other Systems
|
||
|
||
| System | Direction | Interface |
|
||
|--------|-----------|-----------|
|
||
| **Combat Logic** | Upstream | Provides `PlayerState` to Combat Logic as hit target. Receives `OnPlayerHit(PlayerDamageResult)` for health subtraction. |
|
||
| **Blood Energy Economy** | Downstream | Emits `OnPlayerDied` → Blood Energy resets to 0. |
|
||
| **Wave Manager** | Downstream | Emits `OnPlayerDied` (wave freezes), `OnPlayerRespawned` (wave resets to current checkpoint wave). |
|
||
| **Enemy AI Logic** | Downstream | Emits `OnPlayerDied` → all enemies enter Cooldown/Idle. Emits `OnPlayerVulnerable` → enemies resume normal behavior. |
|
||
| **Form Switch SM** | Downstream | Emits `OnPlayerDied` → resets form to default (Human) on respawn. |
|
||
| **UI/HUD (L2)** | Downstream | Emits `OnPlayerHealthChanged`, `OnPlayerDied`, `OnPlayerRespawned`, `OnPlayerVulnerable` for HUD display. |
|
||
| **VFX Spawner (L1)** | Downstream | Emits `OnPlayerDied` (death VFX), `OnPlayerRespawned` (respawn VFX). |
|
||
| **Audio Player (L1)** | Downstream | Emits `OnPlayerHit` (pain SFX), `OnPlayerDied` (death sting), `OnPlayerRespawned` (respawn whoosh). |
|
||
|
||
## Formulas
|
||
|
||
### Player Damage Reception
|
||
|
||
The player damage formula is defined as:
|
||
|
||
`newHealth = currentHealth - finalDamage`
|
||
|
||
**Variables:**
|
||
|
||
| Variable | Symbol | Type | Range | Description |
|
||
|----------|--------|------|-------|-------------|
|
||
| Current health | `currentHealth` | float | 0–100 | Pre-hit health |
|
||
| Final damage | `finalDamage` | float | 0–150 | From Combat Logic PlayerDamageResult |
|
||
|
||
**Output Range**: 0–100 | Clamped at 0. If newHealth <= 0 → IsDead = true, OnPlayerDied fires.
|
||
|
||
**Design Intent**: Player applies damage as-received — no armor, no resistance, no mitigation. Transparency: a Brute hit always does exactly 25 damage. The player always knows how many hits they can take. Skill expression is in avoiding/parrying hits, not in building damage resistance.
|
||
|
||
---
|
||
|
||
### Death Threshold
|
||
|
||
The death formula is defined as:
|
||
|
||
`isDead = currentHealth <= 0`
|
||
|
||
**Output**: Boolean. Once true, triggers the full death→respawn sequence. `OnPlayerDied` fires exactly once per death.
|
||
|
||
---
|
||
|
||
### Hit Survival Thresholds (Baseline 100 HP)
|
||
|
||
| Enemy Attack | Damage | Hits to Die |
|
||
|-------------|--------|-------------|
|
||
| Swarm | 6 | 17 |
|
||
| Stalker | 18 | 6 |
|
||
| Brute | 25 | 4 |
|
||
|
||
**Design Intent**: Brute is always lethal in 4 hits — enough to create tension but enough time to learn the parry timing. Swarm chip damage adds up — ignoring them is viable short-term but fatal if sustained.
|
||
|
||
## Edge Cases
|
||
|
||
- **If player takes damage exceeding current health (overkill)**: Health clamps to 0. No negative health. Death triggers once. Overkill damage is not tracked — does not affect respawn.
|
||
- **If player takes damage while IsInvincible=true (respawn i-frames)**: Damage ignored. OnPlayerHit does NOT fire. Health unchanged.
|
||
- **If multiple hits kill the player in the same frame (simultaneous hits from multiple enemies)**: All hits resolve (batched frame). Health drops to 0 or below. OnPlayerDied fires exactly once. Hit events for the same frame after death are still emitted but health stays at 0.
|
||
- **If deltaTime = 0 (game paused)**: Death/respawn timers (Death Freeze, Death Fade, Respawn Fade, Invincibility) all pause. No phase transitions during pause.
|
||
- **If player dies on Wave 1 (first wave, checkpoint = Wave 1 start)**: Normal respawn flow applies. Blood energy already 0 or low. Wave 1 restarts. No special case needed.
|
||
- **If player dies during Invincibility (2s i-frames)**: Cannot happen — player is invincible. No damage received.
|
||
- **If OnPlayerDied fires but OnPlayerRespawned never fires (e.g., game quit during death)**: State is discarded. Next session starts fresh. No partial death state persists.
|
||
- **If MaxHealth is modified (future Skill Tree upgrade) while player is dead**: Applied on respawn. CurrentHealth = new MaxHealth on respawn.
|
||
- **If checkpoint advances (OnWaveChanged) during death animation**: Ignored. Checkpoint only updates while player is in Active state. Death-locks checkpoint at the wave where death occurred.
|
||
|
||
## Dependencies
|
||
|
||
| System | Layer | Type | Hard/Soft | Interface |
|
||
|--------|-------|------|-----------|-----------|
|
||
| **Combat Logic** | L0 | Upstream | Hard | Provides PlayerState for hit detection. Receives OnPlayerHit for health subtraction. Cannot function without damage pipeline. |
|
||
| **Blood Energy Economy** | L0 | Downstream | Hard | Emits OnPlayerDied → resets blood energy to 0. Core death penalty mechanic. |
|
||
| **Wave Manager** | L0 | Downstream | Hard | Emits OnPlayerDied (freeze), OnPlayerRespawned (reset wave). Wave reset is the other core death penalty. |
|
||
| **Enemy AI Logic** | L0 | Downstream | Soft | Emits OnPlayerDied → enemies stop attacking. Works without but enemies attacking a corpse looks broken. |
|
||
| **Form Switch SM** | L0 | Downstream | Soft | Emits OnPlayerDied → form resets to Human on respawn. |
|
||
| **UI/HUD (L2)** | L2 | Downstream | Soft | Emits OnPlayerHealthChanged, OnPlayerDied, OnPlayerRespawned for display. |
|
||
| **VFX Spawner (L1)** | L1 | Downstream | Soft | Emits death/respawn events for visual feedback. |
|
||
| **Audio Player (L1)** | L1 | Downstream | Soft | Emits hit/death/respawn events for audio feedback. |
|
||
| **Level Loader (L1)** | L1 | Upstream | Soft | Receives checkpoint spawn positions from LevelData. |
|
||
|
||
**Hard dependencies**: Combat Logic, Blood Energy Economy, Wave Manager.
|
||
|
||
## Tuning Knobs
|
||
|
||
| Parameter | Default | Safe Range | Too Low | Too High |
|
||
|-----------|---------|------------|---------|----------|
|
||
| `MaxHealth` | 100 | 60–150 | Every hit feels lethal — no learning window | Player immortal — no tension |
|
||
| `DeathFreezeDuration` | 0.3s | 0.1–0.5s | No impact feel — death feels glitchy | Too long — feels unresponsive |
|
||
| `DeathFadeDuration` | 1.0s | 0.5–1.5s | Not enough time for audio cue | Player waiting — frustration builds |
|
||
| `RespawnFadeDuration` | 0.5s | 0.3–1.0s | Disorienting — player doesn't register new position | Dead air before action resumes |
|
||
| `InvincibilityDuration` | 2.0s | 1.0–3.0s | Spawn-camped by enemies at perimeter | Too safe — player can clear enemies during i-frames |
|
||
| `PlayerHitShapeRadius` | 0.5 | 0.3–0.7 | Too hard to hit — attacks whiff unfairly | Too easy to hit — player feels bloated |
|
||
|
||
**Key tuning pair**: `MaxHealth` + enemy `AttackBaseDamage` values (in Enemy AI GDD). Changing MaxHealth changes every hits-to-die threshold. Baseline (non-crit worst case): Swarm=100/6≈17 hits, Stalker=100/18≈6 hits, Brute=100/25=4 hits. Actual hits-to-die varies with player crits, positional guarantees, and form-specific damage.
|
||
|
||
## Visual/Audio Requirements
|
||
|
||
Combat is a visual system — Visual/Audio is REQUIRED.
|
||
|
||
| Event | VFX | Audio |
|
||
|-------|-----|-------|
|
||
| `OnPlayerHit` | Red flash at screen edge from hit direction. Player geometry briefly flashes red. | Pain SFX — varies by damage amount (light grunt <15, heavy grunt ≥15) |
|
||
| `OnPlayerDied` | Screen freeze-frame (0.3s) → player geometry shatters outward in white/silver fragments | Bass drop + silence — distinct from enemy death sounds |
|
||
| `OnPlayerRespawned` | Player geometry reforms from fragments at checkpoint position. White flash → form color transition. | Rising whoosh + heartbeat start |
|
||
| `OnPlayerVulnerable` | Player geometry stops pulsing/flashing. Brief shimmer. | Subtle "ready" chime |
|
||
| `OnPlayerHealthChanged` | Health bar VFX (flash on damage, gentle pulse on low health <25%) | None (too frequent for SFX) |
|
||
|
||
**Invincibility visual**: Player geometry pulses white at 4Hz during i-frames. Intensity decreases over the 2s duration — last 0.5s is barely visible.
|
||
|
||
## UI Requirements
|
||
|
||
| Element | Content | Trigger |
|
||
|---------|---------|---------|
|
||
| Health Bar | CurrentHealth / MaxHealth as horizontal bar. Color: green (>50%), yellow (25-50%), red (<25%). | `OnPlayerHealthChanged` |
|
||
| Death Overlay | Full-screen dark vignette + "You Died" text (1s) | `OnPlayerDied` |
|
||
| Respawn Text | "Again" — centered, fades after 1s | `OnPlayerRespawned` |
|
||
| i-Frame Indicator | Health bar border pulses white during invincibility | `OnPlayerRespawned` → `OnPlayerVulnerable` |
|
||
|
||
## Acceptance Criteria
|
||
|
||
| # | GIVEN | WHEN | THEN |
|
||
|---|-------|------|------|
|
||
| 1 | Player CurrentHealth=100. `PlayerDamageResult { FinalDamage=25 }`. | OnPlayerHit received | CurrentHealth=75. OnPlayerHealthChanged(75, 100) fires. IsDead=false. |
|
||
| 2 | Player CurrentHealth=20. `PlayerDamageResult { FinalDamage=25 }`. | OnPlayerHit received | CurrentHealth=0 (clamped). IsDead=true. OnPlayerDied fires. Death sequence begins. |
|
||
| 3 | Player CurrentHealth=10. Two simultaneous hits: FinalDamage=8 and FinalDamage=6. | Both hits in same frame | CurrentHealth=0. OnPlayerDied fires exactly once. Both OnPlayerHit events fire. |
|
||
| 4 | Player IsInvincible=true (during respawn i-frames). `PlayerDamageResult { FinalDamage=25 }`. | OnPlayerHit received | Damage ignored. CurrentHealth unchanged. OnPlayerHealthChanged does NOT fire. |
|
||
| 5 | Player dies. Death Freeze(0.3s) → Death Fade(1.0s) → Respawn Fade(0.5s) → Invincibility(2.0s). | Death sequence completes | Total elapsed ~3.8s. Player is at checkpoint position, full health, form=Human. |
|
||
| 6 | Player dies on Wave 3. Checkpoint = Wave 3. | Respawn completes | Wave Manager.OnPlayerRespawned fires → Wave 3 resets from InterWave. Blood energy = 0. |
|
||
| 7 | Player in Active state. Wave 4 starts (OnWaveChanged fires). | Checkpoint update | CurrentCheckpointWave = 4. Future death respawns at Wave 4. |
|
||
| 8 | deltaTime=0.0 during Death Fade (0.5s elapsed, 0.5s remaining). | Update called | Timer does not advance. Death Fade stays at 0.5s remaining. |
|
||
| 9 | Player CurrentHealth=100. MaxHealth increased to 120 (future upgrade). | Upgrade applied | MaxHealth=120. CurrentHealth stays 100. (Does not auto-heal on upgrade.) |
|
||
| 10 | Player dies at MaxHealth=120. | Respawn completes | CurrentHealth = MaxHealth = 120 (full heal on respawn). |
|
||
|
||
## Open Questions
|
||
|
||
- Respawn position within arena: Should the player respawn at arena center, or at a designer-specified spawn point per wave? MVP defaults to arena center. Defer spawn point customization to Level Loader GDD.
|
||
- Should there be a "death streak" mechanic (e.g., slight health buff after 3 consecutive deaths on same wave)? Aligns with Celeste philosophy of helping struggling players, but may conflict with "Skill Over Stats" pillar.
|
||
- Player HitShape (Circle r=0.5): Is this the same regardless of current form? Or should HitShape change with form (Human smaller, Wolf larger, Mist diffuse)? MVP uses uniform HitShape for simplicity.
|
||
- Should the death counter be tracked for scoring/statistics? Score Calculator GDD may want this data.
|
||
- Game Over condition: Is there one? Infinite retries is the MVP plan, but should a level have a max-death count for challenge mode? Defer to Score Calculator / Menu System.
|