import type { Vector3Tuple } from "./dungeonTypes"; // Version 2 preserves consumed action execution order so durable state can be // reconstructed without reviving attempt-scoped object/flag overwrites. export const DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION = 2; export type DungeonEncounterResetScope = "boss-wipe" | "checkpoint" | "dungeon"; export type DungeonEncounterValue = string | number | boolean; export interface DungeonEncounterFacts { readonly registeredEntityIds?: readonly string[]; readonly deadEntityIds?: readonly string[]; } export type DungeonEncounterEvent = | { readonly kind: "tick"; readonly now: number; readonly facts?: DungeonEncounterFacts; } | { readonly kind: "signal"; readonly now: number; readonly signalId: string; readonly sourceEntityId?: string; } | { readonly kind: "entity-engaged"; readonly now: number; readonly entityId: string } | { readonly kind: "entity-death"; readonly now: number; readonly entityId: string } | { readonly kind: "entity-health"; readonly now: number; readonly entityId: string; readonly healthPercent: number; } | { readonly kind: "interact"; readonly now: number; readonly objectId: string; readonly actorId?: string; }; export type DungeonEncounterTrigger = | { readonly kind: "tick" } | { readonly kind: "signal"; readonly signalId: string } | { readonly kind: "entity-engaged"; readonly entityId: string } | { readonly kind: "entity-death"; readonly entityId: string } | { readonly kind: "entity-health"; readonly entityId: string; readonly atOrBelowPercent: number; } | { readonly kind: "interact"; readonly objectId: string } | { readonly kind: "elapsed"; readonly afterMs: number } | { readonly kind: "entities-dead"; readonly entityIds: readonly string[]; /** * Missing-actor recovery window. It may advance presentation-only rules * when no listed target is registered alive, but can never award an * objective, checkpoint, loot, or dungeon completion. */ readonly timeoutMs?: number; }; export type DungeonEncounterGuard = | { readonly kind: "phase"; readonly phases: readonly string[] } | { readonly kind: "difficulty"; readonly difficultyIds: readonly string[] } | { readonly kind: "flag"; readonly flagId: string; readonly equals: boolean } | { readonly kind: "counter"; readonly counterId: string; readonly comparison: "equal" | "at-least" | "at-most"; readonly value: number; } | { readonly kind: "objective-complete"; readonly objectiveId: string; readonly equals: boolean } | { readonly kind: "spawn-group-active"; readonly spawnGroupId: string; readonly equals: boolean } | { readonly kind: "world-object-state"; readonly objectId: string; readonly states: readonly string[] }; export type DungeonEncounterActionPersistence = "attempt" | "checkpoint" | "run"; interface DungeonEncounterActionBase { /** Stable, encounter-local id used to make rule execution idempotent. */ readonly id: string; readonly persistence?: DungeonEncounterActionPersistence; } export type DungeonEncounterAction = | (DungeonEncounterActionBase & { readonly kind: "set-phase"; readonly phase: string }) | (DungeonEncounterActionBase & { readonly kind: "set-flag"; readonly flagId: string; readonly value: boolean }) | (DungeonEncounterActionBase & { readonly kind: "set-counter"; readonly counterId: string; readonly value: number }) | (DungeonEncounterActionBase & { readonly kind: "increment-counter"; readonly counterId: string; readonly amount?: number }) | (DungeonEncounterActionBase & { readonly kind: "start-timer"; readonly timerId: string; readonly durationMs: number }) | (DungeonEncounterActionBase & { readonly kind: "clear-timer"; readonly timerId: string }) | (DungeonEncounterActionBase & { readonly kind: "activate-spawn-group"; readonly spawnGroupId: string }) | (DungeonEncounterActionBase & { readonly kind: "deactivate-spawn-group"; readonly spawnGroupId: string }) | (DungeonEncounterActionBase & { readonly kind: "set-world-object-state"; readonly objectId: string; readonly state: string }) | (DungeonEncounterActionBase & { readonly kind: "set-transition-active"; readonly transitionId: string; readonly active: boolean }) | (DungeonEncounterActionBase & { readonly kind: "complete-objective"; readonly objectiveId: string }) | (DungeonEncounterActionBase & { readonly kind: "set-checkpoint"; readonly checkpointId: string }) | (DungeonEncounterActionBase & { readonly kind: "teleport-party"; readonly destination: Vector3Tuple }) | (DungeonEncounterActionBase & { readonly kind: "grant-loot"; readonly lootTableId: string }) | (DungeonEncounterActionBase & { readonly kind: "emit-signal"; readonly signalId: string }); export interface DungeonEncounterRule { readonly id: string; readonly trigger: DungeonEncounterTrigger; readonly guards?: readonly DungeonEncounterGuard[]; readonly actions: readonly DungeonEncounterAction[]; /** Defaults to true. Repeatable rules must remain safe when evaluated again. */ readonly once?: boolean; readonly priority?: number; } export interface DungeonEncounterInitialState { readonly flags?: Readonly>; readonly counters?: Readonly>; readonly worldObjectStates?: Readonly>; readonly transitionStates?: Readonly>; } export interface DungeonEncounterResetPolicy { readonly bossWipePhase?: string; readonly checkpointPhase?: string; } export type DungeonEncounterFidelity = | "source-exact" | "source-backed-adapted" | "healer-man-authored"; export type DungeonEncounterScope = "boss-progression" | "instance-orchestration"; /** * Review record kept beside executable encounter data. Adapted behavior must * remain explicit so a safe approximation is never mistaken for a finished * port of the original instance script. */ export interface DungeonEncounterSourceReview { readonly fidelity: DungeonEncounterFidelity; readonly scope: DungeonEncounterScope; readonly files: readonly string[]; readonly reviewedBehavior: string; readonly adaptations: readonly string[]; readonly remainingGaps: readonly string[]; } export interface DungeonEncounterDefinition { readonly id: string; readonly dungeonId: string; readonly initialPhase: string; readonly initialState?: DungeonEncounterInitialState; readonly resetPolicy?: DungeonEncounterResetPolicy; readonly source?: DungeonEncounterSourceReview; readonly rules: readonly DungeonEncounterRule[]; } /** JSON-safe source of truth; renderer objects and callbacks never enter this state. */ export interface DungeonEncounterSnapshot { readonly schemaVersion: typeof DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION; readonly encounterId: string; readonly phase: string; readonly phaseStartedAt: number; readonly flags: Readonly>; readonly counters: Readonly>; readonly timers: Readonly>; readonly worldObjectStates: Readonly>; readonly transitionStates: Readonly>; readonly activeSpawnGroupIds: readonly string[]; readonly completedObjectiveIds: readonly string[]; readonly consumedActionIds: readonly string[]; readonly checkpointId: string | null; readonly revision: number; } export type DungeonEncounterEffect = | { readonly kind: "spawn-group"; readonly actionId: string; readonly spawnGroupId: string } | { readonly kind: "despawn-group"; readonly actionId: string; readonly spawnGroupId: string } | { readonly kind: "world-object-state"; readonly actionId: string; readonly objectId: string; readonly state: string } | { readonly kind: "transition-state"; readonly actionId: string; readonly transitionId: string; readonly active: boolean } | { readonly kind: "party-teleport"; readonly actionId: string; readonly destination: Vector3Tuple } | { readonly kind: "loot-grant"; readonly actionId: string; readonly lootTableId: string } | { readonly kind: "signal"; readonly actionId: string; readonly signalId: string }; export interface DungeonEncounterReduction { readonly snapshot: DungeonEncounterSnapshot; readonly effects: readonly DungeonEncounterEffect[]; readonly firedRuleIds: readonly string[]; } export interface DungeonEncounterEffectEnvelope { readonly dungeonId: string; readonly encounterId: string; readonly effect: DungeonEncounterEffect; }