import { CheckResult } from './rng.js'; import { Condition, ConditionType } from './conditions.js'; import { SizeCategory, GridBounds } from '../../schema/encounter.js'; /** * Character interface for combat participants * * D&D 5e legendary creature properties: * - legendaryActions: Total actions available (usually 3) * - legendaryActionsRemaining: Actions left this round (resets at start of their turn) * - legendaryResistances: Total resistances (usually 3/day) * - legendaryResistancesRemaining: Resistances left (does NOT reset between rounds) * - hasLairActions: Whether this creature can use lair actions on initiative 20 * * Spatial combat properties (Phase 1-4): * - position: Current grid position * - movementSpeed: Base speed in feet (default 30) * - movementRemaining: Remaining movement this turn * - size: Creature size category (affects footprint) */ export interface CombatParticipant { id: string; name: string; initiativeBonus: number; initiative?: number; isEnemy?: boolean; hp: number; maxHp: number; conditions: Condition[]; position?: { x: number; y: number; z?: number; }; movementSpeed?: number; movementRemaining?: number; size?: SizeCategory; hasDashed?: boolean; resistances?: string[]; vulnerabilities?: string[]; immunities?: string[]; reactionUsed?: boolean; hasDisengaged?: boolean; actionUsed?: boolean; bonusActionUsed?: boolean; spellsCast?: { action?: number; bonus?: number; reaction?: number; }; legendaryActions?: number; legendaryActionsRemaining?: number; legendaryResistances?: number; legendaryResistancesRemaining?: number; hasLairActions?: boolean; abilityScores?: { strength: number; dexterity: number; constitution: number; intelligence: number; wisdom: number; charisma: number; }; deathSaveSuccesses?: number; deathSaveFailures?: number; isStabilized?: boolean; isDead?: boolean; ac?: number; attackDamage?: string; attackBonus?: number; } /** * Combat state tracking */ export interface CombatState { participants: CombatParticipant[]; turnOrder: string[]; currentTurnIndex: number; round: number; terrain?: { obstacles: string[]; difficultTerrain?: string[]; water?: string[]; }; props?: Array<{ id: string; position: string; label: string; propType: 'structure' | 'cover' | 'climbable' | 'hazard' | 'interactive' | 'decoration'; heightFeet?: number; cover?: 'none' | 'half' | 'three_quarter' | 'full'; climbable?: boolean; climbDC?: number; breakable?: boolean; hp?: number; currentHp?: number; description?: string; }>; gridBounds?: GridBounds; hasLairActions?: boolean; lairOwnerId?: string; } /** * Result of a combat action with full transparency */ export interface CombatActionResult { type: 'attack' | 'heal' | 'damage' | 'save'; actor: { id: string; name: string; }; target: { id: string; name: string; hpBefore: number; hpAfter: number; maxHp: number; }; attackRoll?: CheckResult; damage?: number; damageRolls?: number[]; healAmount?: number; success: boolean; defeated: boolean; message: string; detailedBreakdown: string; } /** * Result of a legendary action use */ export interface LegendaryActionResult { success: boolean; remaining: number; error?: string; } /** * Result of a legendary resistance use */ export interface LegendaryResistanceResult { success: boolean; remaining: number; error?: string; } /** * MED-003: Result of a death saving throw */ export interface DeathSaveResult { roll: number; isNat20: boolean; isNat1: boolean; success: boolean; successes: number; failures: number; isStabilized: boolean; isDead: boolean; regainedHp: boolean; } export interface EventEmitter { publish(topic: string, payload: any): void; } /** * Combat Engine for managing RPG combat encounters * Handles initiative, turn order, and combat flow * * Now supports D&D 5e legendary creatures: * - Legendary Actions (usable at end of other creatures' turns) * - Legendary Resistances (auto-succeed failed saves) * - Lair Actions (trigger on initiative count 20) */ export declare class CombatEngine { private rng; private state; private emitter?; constructor(seed: string, emitter?: EventEmitter); /** * Start a new combat encounter * Rolls initiative for all participants and establishes turn order * * If any participant has hasLairActions=true, adds 'LAIR' to turn order at initiative 20 */ startEncounter(participants: CombatParticipant[]): CombatState; /** * Auto-detect if a participant is an enemy based on ID/name patterns * * IMPORTANT: UUIDs (like "9e48fa16-0ee4-4b99-a1e0-a162528d1e24") are typically * player characters created via the UI. Pattern-based IDs (like "goblin-1", * "orc-archer-2") are typically spawned enemies. Default to false for UUIDs. */ private detectIsEnemy; /** * Get the current state */ getState(): CombatState | null; /** * Load an existing combat state */ loadState(state: CombatState): void; /** * Get the participant whose turn it currently is * Returns null if it's LAIR's turn */ getCurrentParticipant(): CombatParticipant | null; /** * Check if it's currently the LAIR's turn (initiative 20) */ isLairActionPending(): boolean; /** * Check if a legendary creature can use a legendary action * Rules: Can only use at the end of another creature's turn, not their own */ canUseLegendaryAction(participantId: string): boolean; /** * Use a legendary action * @param participantId - ID of the legendary creature * @param cost - How many legendary actions this use costs (default 1) * @returns Result with success status and remaining actions */ useLegendaryAction(participantId: string, cost?: number): LegendaryActionResult; /** * Use a legendary resistance to automatically succeed on a failed save * Unlike legendary actions, these do NOT reset each round */ useLegendaryResistance(participantId: string): LegendaryResistanceResult; /** * Reset legendary actions for a participant (called at start of their turn) */ private resetLegendaryActions; /** * Advance to the next turn * Returns the participant whose turn it now is */ nextTurn(): CombatParticipant | null; /** * HIGH-002: Calculate damage after applying resistance/vulnerability/immunity */ private calculateDamageWithModifiers; /** * Execute an attack with full transparency * Returns detailed breakdown of what happened */ executeAttack(actorId: string, targetId: string, attackBonus: number, dc: number, damage: number | string, damageType?: string): CombatActionResult; /** * Execute a heal action */ executeHeal(actorId: string, targetId: string, amount: number): CombatActionResult; /** * Pathfinder 2e: Make a check and return degree of success */ makeCheck(modifier: number, dc: number): 'critical-failure' | 'failure' | 'success' | 'critical-success'; /** * Make a detailed check exposing all dice mechanics */ makeCheckDetailed(modifier: number, dc: number): CheckResult; /** * Apply damage to a participant */ applyDamage(participantId: string, damage: number): void; /** * Heal a participant * MED-003: Also resets death saves if healing from 0 HP */ heal(participantId: string, amount: number): void; /** * MED-003: Roll a death saving throw for a participant at 0 HP * D&D 5e Rules: * - Roll d20 * - 10+ = success * - 9 or less = failure * - Natural 20 = regain 1 HP (conscious again) * - Natural 1 = counts as 2 failures * - 3 successes = stabilized (unconscious but won't die) * - 3 failures = dead */ rollDeathSave(participantId: string): DeathSaveResult | null; /** * MED-003: Apply damage at 0 HP (causes automatic death save failures) * D&D 5e Rules: Taking damage at 0 HP = 1 failure (crit = 2 failures) */ applyDamageAtZeroHp(participantId: string, isCritical?: boolean): void; /** * Check if a participant is still conscious (hp > 0) */ isConscious(participantId: string): boolean; /** * Get count of conscious participants */ getConsciousCount(): number; /** * Apply a condition to a participant */ applyCondition(participantId: string, condition: Omit): Condition; /** * Remove a specific condition instance by ID */ removeCondition(participantId: string, conditionId: string): boolean; /** * Remove all conditions of a specific type from a participant */ removeConditionsByType(participantId: string, type: ConditionType): number; /** * Check if a participant has a specific condition type */ hasCondition(participantId: string, type: ConditionType): boolean; /** * Get all conditions on a participant */ getConditions(participantId: string): Condition[]; /** * HIGH-003: Reset reaction and disengage status at start of turn */ private resetTurnResources; /** * Process start-of-turn condition effects */ private processStartOfTurnConditions; /** * Process end-of-turn condition effects */ private processEndOfTurnConditions; /** * Get saving throw bonus for a participant */ private getSaveBonus; /** * Enhanced nextTurn with condition processing and legendary action reset * Now auto-skips dead participants (HP <= 0) */ nextTurnWithConditions(): CombatParticipant | null; /** * Check if a participant can take actions (not incapacitated) */ canTakeActions(participantId: string): boolean; /** * Check if a participant can take reactions */ canTakeReactions(participantId: string): boolean; /** * Validate Action Economy rules * Handles: Action/Bonus Action availability and "Bonus Action Spell" rule */ validateActionEconomy(participantId: string, actionType: 'action' | 'bonus' | 'reaction', spellLevel?: number): { valid: boolean; error?: string; }; /** * Commit an action to the economy tracking */ commitAction(participantId: string, actionType: 'action' | 'bonus' | 'reaction', spellLevel?: number): void; /** * HIGH-003: Check if two positions are adjacent (within 1 tile - 8-directional) */ isAdjacent(pos1: { x: number; y: number; }, pos2: { x: number; y: number; }): boolean; /** * HIGH-003: Get adjacent enemies that could make opportunity attacks * @param moverId - The creature that is moving * @param fromPos - Starting position * @param toPos - Target position * @returns Array of participants who can make opportunity attacks */ getOpportunityAttackers(moverId: string, fromPos: { x: number; y: number; }, toPos: { x: number; y: number; }): CombatParticipant[]; /** * HIGH-003: Execute an opportunity attack * Uses simplified attack: d20 + attacker's initiative bonus vs target's initiative + 10 * Damage is fixed at 1d6 + 2 for simplicity */ executeOpportunityAttack(attackerId: string, targetId: string): CombatActionResult; /** * HIGH-003: Mark a participant as having taken the disengage action */ disengage(participantId: string): void; /** * Check if attacks against a participant have advantage */ attacksAgainstHaveAdvantage(participantId: string): boolean; /** * Check if a participant's attacks have disadvantage */ attacksHaveDisadvantage(participantId: string): boolean; } //# sourceMappingURL=engine.d.ts.map