import { MutableSignal, mutableSignal } from "@/utils/mutable-signal"; import { Command, CommandRegistry, CommandResult, CommandRunnerContextExport, CommandSchema, createCommandRegistry, createCommandRunnerContext, parseCommandSchema, } from "@/utils/command"; import { PromptValidator } from "@/utils/command/command-runner"; import { Mulberry32RNG, ReadonlyRNG, RNG } from "@/utils/rng"; export interface IGameContext = {}> { get value(): TState; get rng(): ReadonlyRNG; produce(fn: (draft: TState) => undefined): void; produceAsync(fn: (draft: TState) => undefined): Promise; run(input: string): Promise>; runParsed(command: Command): Promise>; prompt: ( def: PromptDef, validator: PromptValidator, currentPlayer?: string | null, ) => Promise; // test only _state: MutableSignal; _commands: CommandRunnerContextExport>; _rng: RNG; } export type IGameContextExport = {}> = Omit, "_state" | "_commands" | "_rng">; export function createGameContext = {}>( commandRegistry: CommandRegistry>, initialState?: TState | (() => TState), ): IGameContext { const stateValue = typeof initialState === "function" ? initialState() : (initialState ?? ({} as TState)); const state = mutableSignal(stateValue); let commands: CommandRunnerContextExport> = null as any; const context: IGameContext = { get value(): TState { return state.value; }, get rng() { return this._rng; }, produce(fn: (draft: TState) => undefined) { return state.produce(fn); }, produceAsync(fn: (draft: TState) => undefined) { return state.produceAsync(fn); }, run(input: string) { return commands.run(input); }, runParsed(command: Command) { return commands.runParsed(command); }, prompt(def, validator, currentPlayer) { return commands.prompt( def.schema, validator, def.hintText, currentPlayer, ); }, _state: state, _commands: commands, _rng: new Mulberry32RNG(), }; context._commands = commands = createCommandRunnerContext( commandRegistry, context, ); return context; } export type PromptDef = { schema: CommandSchema; hintText?: string; }; export function createPromptDef( schema: CommandSchema | string, hintText?: string, ): PromptDef { schema = typeof schema === "string" ? parseCommandSchema(schema) : schema; return { schema, hintText }; } export function createGameCommandRegistry< TState extends Record = {}, >() { return createCommandRegistry>(); }