# API 参考 本文档记录游戏模组开发者需要使用的公共 API。 ## 核心接口 ### `IGameContext` 游戏运行的核心上下文,提供状态访问、随机数、命令执行和提示系统。 ```typescript interface IGameContext = {}> { readonly value: TState; // 当前游戏状态(只读) readonly rng: ReadonlyRNG; // 随机数生成器(只读) // 状态更新 produce(fn: (draft: TState) => void): void; // 同步变更状态(基于 mutative) produceAsync(fn: (draft: TState) => void): Promise; // 异步变更状态(等待中断) // 命令执行 run(input: string): Promise>; // 执行命令字符串 runParsed(command: Command): Promise>; // 执行已解析的命令 // 提示系统 prompt( def: PromptDef, validator: PromptValidator, currentPlayer?: string | null ): Promise; } ``` **使用示例:** ```typescript // 读取状态 const currentPlayer = game.value.currentPlayer; // 同步更新状态 game.produce((state) => { state.score += 10; }); // 异步更新状态(等待动画完成) await game.produceAsync((state) => { state.phase = 'next'; }); // 等待玩家输入 const result = await game.prompt( prompts.move, (from, to) => { if (!isValidMove(from, to)) { throw '无效移动'; } return { from, to }; } ); ``` --- ### `GameModule` 游戏模块的类型定义,这是开发者创建游戏时需要导出的核心结构。 ```typescript type GameModule, TResult = unknown> = { registry?: CommandRegistry>; // 可选的命令注册表 createInitialState: () => TState; // 创建初始状态 start: (ctx: IGameContext) => Promise; // 游戏主循环 } ``` --- ### `createGameCommandRegistry()` 创建游戏命令注册表,游戏模组用它来注册自定义命令。 ```typescript function createGameCommandRegistry = {}>(): CommandRegistry> ``` **使用示例:** ```typescript import { createGameCommandRegistry, IGameContext } from '@/index'; export type GameState = { score: number }; export const registry = createGameCommandRegistry(); // 注册命令 registry.register('addScore ', async function(ctx, amount) { ctx.produce((state) => { state.score += amount; }); return { success: true, result: undefined }; }); ``` --- ## 提示系统 ### `createPromptDef(schema, hintText?)` 从字符串模式创建 `PromptDef`。 ```typescript function createPromptDef( schema: CommandSchema | string, hintText?: string ): PromptDef ``` **使用示例:** ```typescript import { createPromptDef } from '@/index'; export const prompts = { // 必需参数 play: createPromptDef<[PlayerType, number, number]>( 'play ', '选择下子位置' ), // 可选参数 draw: createPromptDef<[number?]>( 'draw [count:number]', '抽牌' ), // 带选项 trade: createPromptDef<[string, string]>( 'trade [--force]', '交易' ), }; ``` --- ### `PromptDef` 提示定义,用于 `context.prompt()` 方法。 ```typescript type PromptDef = { schema: CommandSchema; // 命令模式定义 hintText?: string; // 可选的提示文本 } ``` --- ### `PromptValidator` 提示验证函数类型。验证器函数接收解析后的参数,应返回结果或抛出字符串错误。 ```typescript type PromptValidator = (...params: TArgs) => TResult; ``` **验证器规则:** - 返回任意值表示验证成功,该值将作为 `prompt()` 的返回值 - 抛出字符串错误表示验证失败,错误消息会返回给玩家,玩家可重新输入 - 玩家取消输入时,`prompt()` 会抛出异常 --- ### `PromptEvent` 提示事件对象,通过 `commandRunnerContext.on('prompt', handler)` 监听。 ```typescript type PromptEvent = { schema: CommandSchema; hintText?: string; currentPlayer: string | null; tryCommit: (commandOrInput: Command | string) => string | null; // null=成功,string=错误消息 cancel: (reason?: string) => void; } ``` --- ## 零件系统 (Part) ### `Part` 游戏中的可操作物件(棋子、卡牌、骰子等)。 ```typescript type Part = { id: string; // 唯一标识 sides?: number; // 总面数(用于骰子/多面牌) side?: number; // 当前面 alignments?: string[]; // 可用对齐方式 alignment?: string; // 当前对齐方式 regionId: string; // 所属区域 ID position: number[]; // 在区域中的位置坐标 } & Immutable; // 自定义元数据(不可变) ``` **使用示例:** ```typescript import { Part } from '@/index'; export type PieceMeta = { owner: 'X' | 'O'; type: 'pawn' | 'king'; }; export type Piece = Part; // 访问元数据 const piece: Piece = ...; console.log(piece.owner); // 'X' console.log(piece.type); // 'pawn' ``` --- ### 零件操作函数 #### `flip(part)` 翻转到下一面(循环)。 ```typescript function flip(part: Part): void ``` #### `flipTo(part, side)` 翻转到指定面。 ```typescript function flipTo(part: Part, side: number): void ``` #### `roll(part, rng)` 用 RNG 随机掷骰子。 ```typescript function roll(part: Part, rng: RNG): void ``` --- ## 零件工厂 (Part Factory) ### `createParts(item, getId, count?)` 创建多个相同类型的零件。 ```typescript function createParts( item: T, getId: (index: number) => string, count?: number ): Record> ``` **使用示例:** ```typescript import { createParts } from '@/index'; const pieces = createParts( { owner: 'X', type: 'pawn' }, (i) => `piece-x-${i}`, 5 // 创建 5 个 ); ``` --- ### `createPartsFromTable(items, getId, getCount?)` 从配置表批量创建零件。 ```typescript function createPartsFromTable( items: readonly T[], getId: (item: T, index: number) => string, getCount?: ((item: T) => number) | number ): Record> ``` **使用示例:** ```typescript import { createPartsFromTable } from '@/index'; const cardTable = [ { name: 'fireball', damage: 3 }, { name: 'shield', defense: 2 }, ]; const cards = createPartsFromTable( cardTable, (item) => item.name, (item) => item.name === 'fireball' ? 4 : 2 // 每种卡牌的数量 ); ``` --- ## 区域系统 (Region) ### `Region` 游戏区域(棋盘、手牌区等)。 ```typescript type Region = { id: string; // 区域 ID axes: RegionAxis[]; // 坐标轴定义 childIds: string[]; // 包含的零件 ID 列表 partMap: Record; // 位置 -> 零件 ID 映射 } ``` --- ### `RegionAxis` 区域的一个坐标轴。 ```typescript type RegionAxis = { name: string; min?: number; max?: number; align?: 'start' | 'end' | 'center'; // 对齐方式 } ``` --- ### `createRegion(id, axes)` 创建区域。 ```typescript function createRegion(id: string, axes: RegionAxis[]): Region ``` **使用示例:** ```typescript import { createRegion, createRegionAxis } from '@/index'; // 创建 3x3 棋盘 const board = createRegion('board', [ createRegionAxis('row', 0, 2), createRegionAxis('col', 0, 2), ]); // 或简写 const board = createRegion('board', [ { name: 'row', min: 0, max: 2 }, { name: 'col', min: 0, max: 2 }, ]); ``` --- ### `createRegionAxis(name, min?, max?, align?)` 创建坐标轴。 ```typescript function createRegionAxis( name: string, min?: number, max?: number, align?: 'start' | 'end' | 'center' ): RegionAxis ``` --- ### 区域操作函数 #### `applyAlign(region, parts)` 根据轴的 `align` 配置重新排列零件位置。 ```typescript function applyAlign(region: Region, parts: Record>): void ``` #### `shuffle(region, parts, rng)` 在区域内随机打乱零件位置。 ```typescript function shuffle(region: Region, parts: Record>, rng: RNG): void ``` #### `moveToRegion(part, sourceRegion, targetRegion, position?)` 将零件从一个区域移动到另一个区域。 ```typescript function moveToRegion( part: Part, sourceRegion: Region | null, targetRegion: Region | null, position?: number[] ): void ``` --- ## 命令系统 (Command System) ### `Command` 解析后的命令对象。 ```typescript type Command = { name: string; // 命令名 flags: Record; // 标志(如 --verbose) options: Record; // 选项(如 --player X) params: unknown[]; // 位置参数 } ``` --- ### `CommandSchema` 命令模式定义,用于验证和解析。 ```typescript type CommandSchema = { name: string; params: CommandParamSchema[]; options: Record; flags: Record; } ``` --- ### `CommandResult` 命令执行结果(判别联合类型)。 ```typescript type CommandResult = | { success: true; result: T } | { success: false; error: string } ``` **使用示例:** ```typescript const result = await game.run('move piece1 piece2'); if (result.success) { console.log('命令执行成功', result.result); } else { console.error('命令执行失败', result.error); } ``` --- ### `CommandDef` 命令定义对象,用于 `registry.register()`。 ```typescript type CommandDef> = { schema: string | CommandSchema; run: TFunc; } type CommandFunction = (ctx: TContext, ...args: any[]) => Promise; ``` --- ### `CommandRegistry` 命令注册表。 ```typescript class CommandRegistry extends Map> { register( ...args: [schema: CommandSchema | string, run: TFunc] | [CommandDef] ): (ctx, ...args) => Promise } ``` **注册命令的两种方式:** ```typescript // 方式 1:直接传入 schema 和函数 registry.register('move ', async function(ctx, from, to) { ctx.produce((state) => { /* 修改状态 */ }); return { success: true, result: undefined }; }); // 方式 2:使用 CommandDef 对象 registry.register({ schema: 'move ', run: async function(ctx, from, to) { ctx.produce((state) => { /* 修改状态 */ }); return { success: true, result: undefined }; } }); ``` --- ### `parseCommand(input, schema?)` 解析命令字符串为 `Command` 对象。 ```typescript function parseCommand(input: string, schema?: CommandSchema): Command ``` --- ### `parseCommandSchema(schemaStr, name?)` 从字符串模式解析命令模式。 ```typescript function parseCommandSchema(schemaStr: string, name?: string): CommandSchema ``` **Schema 语法:** - `` - 必需参数 - `[param]` - 可选参数 - `[param:type]` - 带类型验证的参数(如 `[count:number]`) - `--option:value` - 必需选项 - `[-o value]` - 可选选项 - `[--flag]` - 可选标志 --- ## 随机数生成器 (RNG) ### `ReadonlyRNG` 只读 RNG 接口(`IGameContext.rng` 返回此类型)。 ```typescript interface ReadonlyRNG { next(max?: number): number; // [0,1) 随机数,或 [0,max) nextInt(max: number): number; // [0,max) 随机整数 } ``` --- ### `RNG` 可设置种子的 RNG 接口。 ```typescript interface RNG extends ReadonlyRNG { setSeed(seed: number): void; getSeed(): number; } ``` **使用示例:** ```typescript // 在 IGameContext 中使用 const roll = game.rng.nextInt(6) + 1; // 1-6 的随机数 // 在区域操作中使用时 shuffle(region, parts, rng); // 需要传入 RNG ``` --- ## 可变信号 (MutableSignal) ### `MutableSignal` 扩展自 Preact Signal 的可变信号类,支持 mutative-style 的 `produce` 方法。 ```typescript class MutableSignal extends Signal { produce(fn: (draft: T) => void): void; addInterruption(promise: Promise): void; // 添加中断 Promise(用于动画等待) clearInterruptions(): void; // 清除所有中断 produceAsync(fn: (draft: T) => void): Promise; // 等待中断后更新状态 } ``` --- ### `mutableSignal(initial?, options?)` 创建可变信号。 ```typescript function mutableSignal(initial?: T, options?: SignalOptions): MutableSignal ``` --- ## 游戏主机 (GameHost) ### `GameHost` 游戏会话的生命周期管理器。 ```typescript class GameHost { readonly state: ReadonlySignal; // 游戏状态(响应式) readonly status: ReadonlySignal; // 运行状态 readonly activePromptSchema: ReadonlySignal; // 当前活动提示的模式 readonly activePromptPlayer: ReadonlySignal; // 当前等待输入的玩家 readonly activePromptHint: ReadonlySignal; // 当前提示文本 tryInput(input: string): string | null; // 尝试提交输入,返回错误信息或 null tryAnswerPrompt(def: PromptDef, ...args: TArgs): void; // 尝试回答提示 addInterruption(promise: Promise): void; // 注册中断 Promise(用于动画) clearInterruptions(): void; // 清除所有中断 start(seed?: number): Promise; // 启动游戏 dispose(): void; // 销毁游戏 on(event: 'start' | 'dispose', listener: () => void): () => void; // 注册事件监听 } ``` --- ### `GameHostStatus` ```typescript type GameHostStatus = 'created' | 'running' | 'disposed'; ``` --- ### `createGameHost(gameModule)` 从游戏模块创建 `GameHost` 实例。 ```typescript function createGameHost>( gameModule: GameModule ): GameHost ``` **使用示例:** ```typescript import { createGameHost } from '@/index'; import { gameModule } from './my-game'; const host = createGameHost(gameModule); // 启动游戏 const result = await host.start(42); // 传入种子 // 提交玩家输入 const error = host.tryInput('play X 0 0'); if (error) { console.error('输入错误:', error); } // 监听事件 host.on('start', () => console.log('游戏开始')); host.on('dispose', () => console.log('游戏结束')); // 销毁游戏 host.dispose(); ``` --- ## Preact Signals 重新导出 ```typescript export * from '@preact/signals-core'; ``` 开发者可直接使用 `@preact/signals-core` 的所有导出,包括: - `Signal` - 基础信号类 - `ReadonlySignal` - 只读信号类型 - `signal(value)` - 创建信号 - `computed(fn)` - 创建计算信号 - `effect(fn)` - 创建副作用 - `batch(fn)` - 批量更新 - `untracked(fn)` - 非追踪读取 --- ## 测试辅助函数 以下函数主要用于测试代码: ### `createGameContext(options)` 创建游戏上下文实例。 ```typescript function createGameContext(options: { initialState: TState; registry?: CommandRegistry>; rng?: RNG; }): IGameContext ``` **使用示例:** ```typescript import { createGameContext } from '@/core/game'; import { createInitialState, registry } from './my-game'; const ctx = createGameContext({ initialState: createInitialState(), registry, }); // 执行命令 await ctx.run('move piece1 piece2'); // 断言状态 expect(ctx.value.score).toBe(10); ``` --- ### `createTestContext()` 创建用于测试的游戏上下文(简化版)。 ```typescript function createTestContext(initialState: TState): IGameContext ``` --- ### `createTestRegion()` 创建用于测试的区域。 ```typescript function createTestRegion(): Region ```