import { Kernel, makeKernel } from './kernel/Kernel' import { World } from './runtime/World' import { GameFacade as ApiFacade, ActName, ActPayload, FacadeEvent } from './runtime/ApiFacade' import { GameState } from '../types/domain' import { GameClock } from './kernel/clock' import { CotycPlugin } from './kernel/plugin' import { DataPack, PACK } from '../data/registry' import { createWorldState } from './runtime/creation' import { normalizeGameState } from './runtime/World' export interface GameEngineOptions { seed?: string datapack?: Partial noAutoCreate?: boolean } export type EngineStatus = 'idle' | 'running' | 'gameover' /** * GameEngine —— 仙途家族志统一游戏引擎(0.1.14 架构核心)。 * 一个引擎对象 = 内核(Kernel: 时钟+随机+总线) + 世界(World) + 门面(ApiFacade) + 存储接口。 * UI/测试/未来工具仅需认识 GameEngine。 */ export class GameEngine { kernel: Kernel readonly datapack: typeof PACK world: World facade: ApiFacade private _seed: string autosaveEvery = 12 private tickSinceSave = 0 constructor(opts: GameEngineOptions = {}) { this._seed = opts.seed ?? `cotyc-${Date.now().toString(36)}-${Math.floor(Math.random() * 1296).toString(36)}` this.kernel = makeKernel(this._seed) this.datapack = PACK if (opts.datapack) this.datapack.override(opts.datapack) // 组装World(内核注入→时钟/随机/总线单源) const state = opts.seed ? createWorldState({ seed: this._seed, surname: '林', familyName: '林氏', motto: '耕读传家,术法继世', difficulty: 'normal' }) : (globalThis as never as { __state?: GameState }).__state this.world = new World(state!, [], this.kernel) this.facade = new ApiFacade(this.world, 1) // Kernel 总线 → World.out 桥接(attached 后同步 feed) this.world.out = this.world.out.concat([]) } view(): GameState { return this.world.state } advance(): void { if (this.world.state.gameOver) return if (this.world.state.pendingEvent) return // 事件待决:停下(用户/自动化应 resolvePending) this.world.advanceMonth() } /** 处理当前待决事件(默认选第一项);无待决则 no-op */ resolvePending(idx = 0): boolean { const pid = this.world.state.pendingEvent if (!pid) return false // 直接走门面 apply 通路的引擎包装(不依赖 UI) return this.world.applyEventChoice(pid, idx) } syncRng(): void { this.world.syncRng() } act(name: ActName, payload: ActPayload): boolean { return this.facade.act(name, payload) } query(ref: string): Record { return this.facade.query(ref) } subscribe(on: (e: FacadeEvent) => void): () => void { return this.facade.subscribe(on) } about(): { title: string; version: string; modules: number; systems: number; plugins: number; packFingerprint: string } { const a = this.facade.about() return { ...a, version: '0.1.14' } } installPlugin(p: CotycPlugin): { ok: boolean; reason?: string } { return this.world.installPlugin(p) } /** 档位快照(存档 JSON) */ snapshot(): GameState { this.world.syncRng() return JSON.parse(JSON.stringify(this.world.state)) as GameState } /** 从快照恢复(可回放)——重建内核以避免时钟双注册,并回置 rng 保真 */ restore(snap: GameState): void { normalizeGameState(snap) // 新内核(同一 seed 派生)+ 回写存档 rng 快照 —— 回放保真 this.kernel = makeKernel(snap.seed) this.kernel.rng.state = { ...snap.rng } const newWorld = new World(snap, [], this.kernel) newWorld.out = this.world.out this.world = newWorld this.facade = new ApiFacade(this.world, 1) } status(): EngineStatus { return this.world.state.gameOver ? 'gameover' : 'running' } seed(): string { return this._seed } } /** 已用旧内核时钟注册的世界快照,反序列化时公用(便捷入口) */ export function engineFromSnapshot(state: GameState): GameEngine { const eng = new GameEngine({ seed: state.seed, noAutoCreate: true }) eng.restore(state) return eng }