# ADR-005: Enemy AI Decision Model **Status**: Accepted **Date**: 2026-04-27 **Engine**: Unity 2022.3.62f3c1 **GDD Requirements Addressed**: Enemy AI Logic, Boss AI Logic (systems-index.md #4, #5) --- ## Context Nightborn's enemy AI doesn't need complex tactical reasoning, but has a special requirement: each enemy type must strongly favor a specific form, creating "I need to switch" pressure. MVP scope is 3-5 enemy types. The AI model must be simple to implement, produce predictable behavior, and be easily understood and tested by external developers. ## Decision **Use lightweight per-type Finite State Machines. Reject Behavior Trees and Utility AI.** ### Base FSM Structure (shared by all enemy types) ``` Idle ──→ Chase ──→ Attack ──→ Cooldown ↑ │ │ └────────────────────┴──────────┘ If too far, return to Chase ``` ### Per-Type Differentiation in the Attack State | Enemy Type | Attack Behavior | Encouraged Form | Why | |------------|----------------|-----------------|-----| | **Swarm** | Many simultaneous small-area hits | Mist | Mist's circular AOE clears groups at once | | **Brute** | Slow, high-damage single strike | Human | Human's parry window is clear and precise against it | | **Stalker** | Fast rush + point-blank flurry | Wolf | Wolf's rectangular dash collides for a stagger counter | | **Shooter** | Ranged projectile barrage from periphery | Mist/Wolf | Mist phases through to close, or Wolf bursts to eliminate | | **Boss** | Multi-phase mixed patterns | All forms | Phase transitions require form adaptation | ### Why Not Behavior Trees BTs excel at complex decision chains (e.g., FPS cover-shooter AI). Nightborn enemies need only 4 states. The tree's maintenance cost exceeds its value. ### Why Not Utility AI Scoring systems produce "optimal" behavior but aren't predictable enough — players can't learn enemy patterns, violating the "Battlefield is Information" pillar. ### Implementation ```csharp // L0 — Pure C#, returns one AICommand per frame public enum EnemyStateType { Idle, Chase, Attack, Cooldown } public class EnemyAILogic { public EnemyTypeConfig Config; public AICommand ComputeAction(EnemyState self, PlayerState player) { TransitionState(self, player); return CurrentState switch { Idle => ScanForPlayer(player), Chase => MoveToward(player), Attack => ExecuteAttack(self.AttackPattern, player), Cooldown => WaitAndReset(self.CooldownTimer), }; } // Per-type override for unique transition logic protected virtual void TransitionState(EnemyState self, PlayerState player) { } } ``` ### Boss Extension Boss AI extends the base FSM with: - `BossPhase` enum (additional state dimension) - Phase transition conditions (health thresholds, timer triggers) - `OnPhaseChanged` event for L1 VFX/Audio synchronization ## Consequences - External developers can understand an enemy's behavior from `EnemyTypeConfig` alone — no need to parse behavior trees - Enemy behavior is predictable and learnable — satisfies "Battlefield is Information" pillar - Each enemy type's Attack pattern naturally points to one form counter — reinforces "Form is Tactics" - If future enemies need more intelligence (cover, coordination), the FSM base can be extended without replacement