import { Awaitable } from "../../../util/data"; import { LiveGame } from "../common/game"; import { CalledActionResult, StackModelWaiting } from "../gameTypes"; import { LogicAction } from "./logicAction"; import { Lambda } from "../elements/condition"; export declare enum StackModelItemType { Action = "action", Link = "link" } /** * Loop type for StackModel * - count: repeat N times * - condition: while condition is true */ export type StackModelLoopType = "count" | "condition"; /** * Loop configuration for StackModel */ export type StackModelLoopConfig = { type: StackModelLoopType; /** Current iteration count (0-based) */ counter: number; /** Max iterations (count loop only) */ limit?: number; /** Action ID containing the condition Lambda (condition loop only, for deserialization) */ conditionActionId?: string; /** Action IDs for the loop body (for deserialization) */ bodyActionIds: string[]; /** Whether the loop has been broken */ broken: boolean; }; /** * Serialized loop configuration */ export type StackModelLoopRawData = { type: StackModelLoopType; counter: number; limit?: number; conditionActionId?: string; bodyActionIds: string[]; broken: boolean; }; /** * Stack item data for serialization */ export type StackModelItemData = { type: StackModelItemType.Action; actionType: string | null; action: string | null; } | { type: StackModelItemType.Link; actionType: string | null; action: string | null; stacks: StackModelRawData[]; stackWaitType: StackModelWaiting["type"] | null; }; /** * Serialized StackModel data with loop support */ export type StackModelRawData = { items: StackModelItemData[]; loop?: StackModelLoopRawData; }; export declare class StackModel { private liveGame; __tag: string | undefined; static isStackModel(action: CalledActionResult | Awaitable | StackModel): action is StackModel; static createStackModel(liveGame: LiveGame, data: StackModelRawData, actionMap: Map): StackModel; /** * Create a count-based loop StackModel (repeat N times) */ static createCountLoop(liveGame: LiveGame, times: number, bodyActions: LogicAction.Actions[]): StackModel; /** * Create a condition-based loop StackModel (while condition is true) */ static createConditionLoop(liveGame: LiveGame, condition: Lambda, conditionActionId: string, bodyActions: LogicAction.Actions[]): StackModel; static isCalledActionResult(action: CalledActionResult | Awaitable | StackModel | undefined | null): action is CalledActionResult; static fromAction(action: LogicAction.Actions): CalledActionResult; static executeStackModelGroup(type: StackModelWaiting["type"], stackModels: StackModel[]): Awaitable; static isStackModelsAwaiting(type: StackModelWaiting["type"], stackModels: StackModel[]): boolean; private stack; private waitingAction; private loopConfig; private loopBodyActions; private loopCondition; private loopStartTime; private loopDebugCheckpoint; constructor(liveGame: LiveGame, tag?: string | undefined); /** * Initialize loop configuration */ private initLoop; /** * Check if the loop should continue */ private shouldContinueLoop; /** * Push loop body actions to stack for next iteration */ private pushLoopBody; /** * Called when an iteration completes, checks if loop should continue */ private onIterationComplete; /** * Check for potential infinite loops in debug mode */ private checkInfiniteLoop; /** * Break the current loop */ breakLoop(): void; /** * Check if this StackModel is a loop */ isLoop(): boolean; /** * Executes the next operation in the stack * * Main responsibilities: * 1. Check and handle waiting states at the top of the stack * 2. Execute current operation and handle its results * 3. Manage asynchronous operations and nested stack models * * Execution flow: * 1. If stack is empty, return null * 2. Check top element: * - If it's an unsettled Awaitable, return the Awaitable * - If it's a waiting operation (with nested stack models), check nested stack status * 3. Pop and execute current operation: * - If it's an Awaitable, wait for completion and handle result * - If it's a regular operation, execute and handle return value * * @returns One of the following: * - CalledActionResult: Execution result (returned a synchronous operation) * - Awaitable: Asynchronous operation * - null: No more operations if the stack is empty, or the top element is exited */ rollNext(): CalledActionResult | Awaitable | null; execute(): Awaitable; abortStackTop(): CalledActionResult | null; getTopSync(): CalledActionResult | null; executeActions(result: CalledActionResult): CalledActionResult | Awaitable | null; isWaiting(): boolean; /** * Serialize current StackModel into a plain JSON-serialisable structure. * * @param frozen - When true (default), the snapshot also contains the * action currently executing at the top of the stack * (waitingAction). This is required by save/load so that * reloading a save resumes exactly at the current dialog or * async node, keeping the runtime state self-consistent. * When false, waitingAction is excluded and the snapshot * reflects the state *before* the current action started. * The undo/history system will then re-insert the action * manually (see LiveGame.undo) to avoid having two copies * of the same action after deserialisation. * @returns Snapshot that can be passed to StackModel.deserialize. */ serialize(frozen?: boolean): StackModelRawData; reset(): void; deserialize(data: StackModelRawData, actionMap: Map): this; isEmpty(): boolean; push(...items: (CalledActionResult | Awaitable)[]): this; }