195 lines
8.4 KiB
TypeScript
195 lines
8.4 KiB
TypeScript
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<Record<string, boolean>>;
|
|
readonly counters?: Readonly<Record<string, number>>;
|
|
readonly worldObjectStates?: Readonly<Record<string, string>>;
|
|
readonly transitionStates?: Readonly<Record<string, boolean>>;
|
|
}
|
|
|
|
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<Record<string, boolean>>;
|
|
readonly counters: Readonly<Record<string, number>>;
|
|
readonly timers: Readonly<Record<string, number>>;
|
|
readonly worldObjectStates: Readonly<Record<string, string>>;
|
|
readonly transitionStates: Readonly<Record<string, boolean>>;
|
|
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;
|
|
}
|