---
description: Combat state and movement logic for turn-based combat loop
globs: src/systems/TurnSystem.js, src/systems/MovementSystem.js, src/types/CombatState.ts, src/types/Actions.ts
alwaysApply: false
---

# **Combat State & Movement Rule**

This rule defines the logic for managing the Turn-Based Combat Loop and the execution of Movement Actions.

## **1. TypeScript Interfaces (Data Models)**

```typescript
export type CombatPhase =
  | "INIT"
  | "TURN_START"
  | "WAITING_FOR_INPUT"
  | "EXECUTING_ACTION"
  | "TURN_END"
  | "COMBAT_END";

export interface CombatState {
  /** Whether combat is currently active */
  isActive: boolean;
  /** Current Round number */
  round: number;
  /** Ordered list of Unit IDs for the current round */
  turnQueue: string[];
  /** The ID of the unit currently taking their turn */
  activeUnitId: string | null;
  /** Current phase of the turn loop */
  phase: CombatPhase;
}

// src/types/Actions.ts

export interface ActionRequest {
  type: "MOVE" | "SKILL" | "ITEM" | "WAIT";
  sourceId: string;
  targetId?: string; // For targeted skills
  targetPosition?: { x: number; y: number; z: number }; // For movement/AoE
  skillId?: string;
  itemId?: string;
}

export interface MovementResult {
  success: boolean;
  path: { x: number; y: number; z: number }[];
  costAP: number;
  finalPosition: { x: number; y: number; z: number };
}
```

## **2. Conditions of Acceptance (CoA)**

These checks ensure the combat loop feels fair and responsive.

### **System: TurnSystem (src/systems/TurnSystem.js)**

- **CoA 1: Initiative Roll:** Upon starting combat, all active units must be sorted into the turnQueue based on their Speed stat (Highest First)
- **CoA 2: Turn Start Hygiene:** When a unit's turn begins:
  - Their `currentAP` must reset to `baseAP`
  - Status effects (DoTs) must tick
  - Cooldowns must decrement
- **CoA 3: Cycling:** Calling `endTurn()` must move the `activeUnitId` to the next in the queue. If the queue is empty, increment round and re-roll/reset the queue

### **System: MovementSystem (src/systems/MovementSystem.js)**

- **CoA 1: Validation:** Moving to a tile must fail if:
  - The tile is blocked/occupied
  - No path exists
  - The unit has insufficient AP for the _entire_ path
- **CoA 2: Execution:** A successful move must:
  - Update the Unit's position in the UnitManager
  - Update the VoxelGrid occupancy map
  - Deduct the correct AP cost (including terrain modifiers)
- **CoA 3: Path Snapping:** If the user clicks a tile, but the unit only has AP to reach halfway, the system should allow moving to the _furthest reachable tile_ on that path (optional QoL)

## **3. Implementation Requirements**

### **The Turn System**

Create `src/systems/TurnSystem.js`. It should manage the CombatState.

1. Implement `startCombat(units)`: Sorts units by speed into turnQueue and sets phase to TURN_START
2. Implement `startTurn()`: Refills the active unit's AP, processes cooldowns/statuses, and sets phase to WAITING_FOR_INPUT
3. Implement `endTurn()`: Rotates the queue. If queue is empty, start new Round
4. It should accept the UnitManager in the constructor to access unit stats
5. Dispatch events: `combat-start`, `turn-start`, `turn-end`

### **The Movement System**

Create `src/systems/MovementSystem.js`. It coordinates Pathfinding, VoxelGrid, and UnitManager.

1. Implement `validateMove(unit, targetPos)`: Returns `{ valid: boolean, cost: number, path: [] }`. It checks `A*` pathfinding and compares cost vs `unit.currentAP`
2. Implement `executeMove(unit, targetPos)`:
   - Validates the move first
   - Updates `grid.moveUnit(unit, targetPos)`
   - Deducts AP
   - Returns a Promise that resolves when the visual movement (optional animation hook) would handle it, or immediately for logic

