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.
This commit is contained in:
hypercross 2026-04-23 10:37:28 +08:00
parent e8c995f74f
commit 23ac09ff21
3 changed files with 19 additions and 45 deletions

View File

@ -12,6 +12,7 @@ export type GameHostStatus = "created" | "running" | "disposed";
export class GameHost<
TState extends Record<string, unknown>,
TResult = unknown,
TModule extends GameModule<TState, TResult> = GameModule<TState, TResult>,
> {
readonly state: ReadonlySignal<TState>;
readonly status: ReadonlySignal<GameHostStatus>;
@ -29,16 +30,16 @@ export class GameHost<
private _eventListeners: Map<"start" | "dispose", Set<() => void>>;
private _isDisposed = false;
constructor(
registry: CommandRegistry<IGameContext<TState>>,
createInitialState: () => TState,
start: (ctx: IGameContext<TState>) => Promise<TResult>,
) {
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<string, unknown>,
TResult = unknown,
>(gameModule: GameModule<TState, TResult>): GameHost<TState, TResult> {
return new GameHost(
gameModule.registry || createGameCommandRegistry(),
gameModule.createInitialState,
gameModule.start,
);
TModule extends GameModule<TState, TResult> = GameModule<TState, TResult>,
>(gameModule: TModule): GameHost<TState, TResult> {
return new GameHost(gameModule);
}

View File

@ -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;
};

View File

@ -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<CombatState> {
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;
}
}