aether-shards/.cursor/rules/logic/CombatState/RULE.md
Matthew Mone 2c86d674f4 Add mission debrief and procedural mission generation features
- Introduce the MissionDebrief component to display after-action reports, including XP, rewards, and squad status.
- Implement the MissionGenerator class to create procedural side missions, enhancing replayability and resource management.
- Update mission schema to include mission objects for INTERACT objectives, improving mission complexity.
- Enhance GameLoop and MissionManager to support new mission features and interactions.
- Add tests for MissionDebrief and MissionGenerator to ensure functionality and integration within the game architecture.
2026-01-01 16:08:54 -08:00

3.7 KiB

description globs alwaysApply
Combat state and movement logic for turn-based combat loop src/systems/TurnSystem.js, src/systems/MovementSystem.js, src/types/CombatState.ts, src/types/Actions.ts 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)

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