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 { RngHub } from './kernel/rng' import { CotycPlugin } from './kernel/plugin' import { DataPack, PACK } from '../data/registry' import { createWorldState } from './runtime/creation' import './runtime/demo-plugins' // side-effect:注册示例插件工厂(持久化重装用) import { normalizeGameState } from './runtime/World' export interface GameEngineOptions { seed?: string datapack?: Partial noAutoCreate?: boolean /** 建档参数(NewGame 透传;缺省林氏兜底) */ surname?: string familyName?: string motto?: string difficulty?: 'easy' | 'normal' | 'hard' } 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 ?? RngHub.rollSeed() this.kernel = makeKernel(this._seed) this.datapack = PACK if (opts.datapack) this.datapack.override(opts.datapack) // 组装World(内核注入→时钟/随机/总线单源;0.1.35 移除 __state 泄漏口——seed 恒用 _seed) if (opts.noAutoCreate) { this.world = new World(createWorldState({ seed: this._seed, surname: '占位', familyName: '占位', motto: '', difficulty: 'normal' }), [], this.kernel) } else { const state = createWorldState({ seed: this._seed, surname: opts.surname?.trim() || '林', familyName: opts.familyName?.trim() || `${opts.surname?.trim() || '林'}氏`, motto: opts.motto?.trim() || '耕读传家,术法继世', difficulty: opts.difficulty ?? 'normal' }) 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 } 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 }