# ADR-004: Form Switch State Machine Design **Status**: Accepted **Date**: 2026-04-27 **Engine**: Unity 2022.3.62f3c1 **GDD Requirements Addressed**: Form Switch State Machine (systems-index.md #3) --- ## Context Form switching is Nightborn's core operation — every switch is a risky commitment action. The state machine must balance "responsive feel" against "decisions have weight." It must also support future switch-speed upgrades from the Skill Tree. ## Decision ### Four-Phase Linear State Machine ``` RequestSwitch(Wolf) Idle ─────────────────────────────→ Windup ↑ │ │ timer >= recovery │ timer >= windup │ │ │ ┌─────────────────────────────┐ │ │ │ Hit during Windup → │←──│ │ │ OnSwitchInterrupted │ │ │ └─────────────────────────────┘ ↓ │ Switching (i-frames) Recovery ←───────────────────────────┘ timer >= switch ``` ### Phase Parameters (baseline, modifiable by Skill Tree) | Phase | Duration | Can Act | Interruptible | Can Re-Switch | |-------|----------|---------|---------------|---------------| | **Idle** | — | Full | — | — | | **Windup** | 0.25s | Cannot attack | Yes (hit → lose Blood Energy) | No | | **Switching** | 0.10s | Cannot act | No (i-frames) | No | | **Recovery** | 0.15s | Can attack/move | No | No | **Total switch time**: 0.50s. Danger window: 0.25s (Windup). ### Interrupt Rules - Hit during Windup → `OnSwitchInterrupted` → Blood Energy already spent, switch fails → return to Idle - This IS the "switched at the wrong time" punishment — core to the Switch is Commitment pillar ### Post-Switch Cooldown After Recovery ends, an additional 0.3s cooldown before another switch is allowed (minimum 0.8s between switches) — prevents switch spam and preserves rhythmic feel. ### Implementation ```csharp // L0 — Pure C#, zero Unity public class FormSwitchStateMachine { public FormType CurrentForm { get; private set; } = FormType.Human; public SwitchPhase Phase { get; private set; } = SwitchPhase.Idle; public float PhaseTimer { get; private set; } // Tunable (Skill Tree can modify) public float WindupDuration = 0.25f; public float SwitchDuration = 0.10f; public float RecoveryDuration = 0.15f; public float CooldownDuration = 0.3f; public SwitchResult RequestSwitch(FormType target, BloodEnergyEconomy energy); public void Update(float deltaTime); public void Interrupt(); public event Action OnFormChanged; public event Action OnPhaseChanged; public event Action OnSwitchInterrupted; } ``` ### Why a Simple Linear FSM - 4 phases progress sequentially — no Hierarchical FSM or Behavior Tree needed - Only Idle accepts input — logic is straightforward for external developers - If a future form needs a different flow, it can subclass with its own state machine ## Consequences - Switch feel is controlled by exactly 4 parameters (WindupDuration, SwitchDuration, RecoveryDuration, CooldownDuration) — tuning is centralized - Interrupt during Windup creates clear "wrong time to switch" punishment, reinforcing Switch is Commitment - Skill Tree can directly modify these 4 parameters for switch-speed upgrades - Windup duration (0.25s) is the critical feel parameter — MUST be validated through prototype, likely iterating between 0.15s–0.35s