From 23ac09ff21f296ccb6ec7d903256ea3155ff70ee Mon Sep 17 00:00:00 2001 From: hypercross Date: Thu, 23 Apr 2026 10:37:28 +0800 Subject: [PATCH] refactor: simplify GameHost constructor to use GameModule Update `GameHost` to accept a `GameModule` directly instead of individual parameters. This simplifies the `createGameHost` factory and improves type safety by preserving the module type. Also apply `readonly` modifiers to `ContentModule` in the slay-the-spire-like sample. --- src/core/game-host.ts | 22 ++++++++--------- src/samples/slay-the-spire-like/data/types.ts | 18 +++++++------- .../system/combat/index.ts | 24 ------------------- 3 files changed, 19 insertions(+), 45 deletions(-) diff --git a/src/core/game-host.ts b/src/core/game-host.ts index 27f1b07..3a64a27 100644 --- a/src/core/game-host.ts +++ b/src/core/game-host.ts @@ -12,6 +12,7 @@ export type GameHostStatus = "created" | "running" | "disposed"; export class GameHost< TState extends Record, TResult = unknown, + TModule extends GameModule = GameModule, > { readonly state: ReadonlySignal; readonly status: ReadonlySignal; @@ -29,16 +30,16 @@ export class GameHost< private _eventListeners: Map<"start" | "dispose", Set<() => void>>; private _isDisposed = false; - constructor( - registry: CommandRegistry>, - createInitialState: () => TState, - start: (ctx: IGameContext) => Promise, - ) { + constructor(public readonly gameModule: TModule) { + const { createInitialState, registry, start } = gameModule; this._createInitialState = createInitialState; this._eventListeners = new Map(); const initialState = createInitialState(); - this._context = createGameContext(registry, initialState); + this._context = createGameContext( + registry ?? createGameCommandRegistry(), + initialState, + ); this._start = start; this.state = this._context._state; @@ -179,10 +180,7 @@ export type GameModule< export function createGameHost< TState extends Record, TResult = unknown, ->(gameModule: GameModule): GameHost { - return new GameHost( - gameModule.registry || createGameCommandRegistry(), - gameModule.createInitialState, - gameModule.start, - ); + TModule extends GameModule = GameModule, +>(gameModule: TModule): GameHost { + return new GameHost(gameModule); } diff --git a/src/samples/slay-the-spire-like/data/types.ts b/src/samples/slay-the-spire-like/data/types.ts index 8c6937a..79e1610 100644 --- a/src/samples/slay-the-spire-like/data/types.ts +++ b/src/samples/slay-the-spire-like/data/types.ts @@ -5,14 +5,14 @@ import type * as desert from "./desert"; import { IRunContext } from "../system/combat"; export type ContentModule = { - getCards: () => desert.Card[]; - getEffects: () => desert.Effect[]; - getEncounters: () => desert.Encounter[]; - getEnemies: () => desert.Enemy[]; - getIntents: () => desert.Intent[]; - getItems: () => desert.Item[]; - getStartingItems: () => desert.Item[]; + readonly getCards: () => readonly desert.Card[]; + readonly getEffects: () => readonly desert.Effect[]; + readonly getEncounters: () => readonly desert.Encounter[]; + readonly getEnemies: () => readonly desert.Enemy[]; + readonly getIntents: () => readonly desert.Intent[]; + readonly getItems: () => readonly desert.Item[]; + readonly getStartingItems: () => readonly desert.Item[]; - dialogues: YarnDialogues; - addTriggers: (triggers: Triggers, run: IRunContext) => void; + readonly dialogues: YarnDialogues; + readonly addTriggers: (triggers: Triggers, run: IRunContext) => void; }; diff --git a/src/samples/slay-the-spire-like/system/combat/index.ts b/src/samples/slay-the-spire-like/system/combat/index.ts index 83d2ba9..9a0799e 100644 --- a/src/samples/slay-the-spire-like/system/combat/index.ts +++ b/src/samples/slay-the-spire-like/system/combat/index.ts @@ -3,27 +3,3 @@ export * from "./factory"; export * from "./prompts"; export * from "./triggers"; export * from "./types"; - -import { GameHost } from "@/core/game-host"; -import { ContentModule } from "../types"; -import { createCommandRegistry } from "@/utils/command"; -import { createStart, createTriggers, Triggers } from "./triggers"; -import { CombatState, IRunContext } from "./types"; - -export class CombatGameHost extends GameHost { - public readonly triggers: Triggers; - constructor( - private module: ContentModule, - private runContext: IRunContext, - private initialState: CombatState, - ) { - let triggers: Triggers; - super( - createCommandRegistry(), - () => initialState, - createStart((triggers = createTriggers(runContext)), runContext), - ); - module.addTriggers(triggers, runContext); - this.triggers = triggers; - } -}