feat(framework): add generic types to GameHostScene
Update GameHostScene to support generic types for TResult and TModule, allowing better type safety when interacting with the game host and its modules.
This commit is contained in:
parent
dfbdaa3499
commit
f893ac9ff7
|
|
@ -1,17 +1,23 @@
|
||||||
import { ReactiveScene } from "./ReactiveScene";
|
import { ReactiveScene } from "./ReactiveScene";
|
||||||
|
|
||||||
import type { GameHost } from "boardgame-core";
|
import type { GameHost, GameModule } from "boardgame-core";
|
||||||
|
|
||||||
export interface GameHostSceneOptions<TState extends Record<string, unknown>> {
|
export interface GameHostSceneOptions<
|
||||||
gameHost: GameHost<TState>;
|
TState extends Record<string, unknown>,
|
||||||
|
TResult,
|
||||||
|
TModule extends GameModule<TState, TResult>,
|
||||||
|
> {
|
||||||
|
gameHost: GameHost<TState, TResult, TModule>;
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class GameHostScene<
|
export abstract class GameHostScene<
|
||||||
TState extends Record<string, unknown>,
|
TState extends Record<string, unknown>,
|
||||||
> extends ReactiveScene<GameHostSceneOptions<TState>> {
|
TResult,
|
||||||
public get gameHost(): GameHost<TState> {
|
TModule extends GameModule<TState, TResult>,
|
||||||
const gameHost = this.initData.gameHost as GameHost<TState>;
|
> extends ReactiveScene<GameHostSceneOptions<TState, TResult, TModule>> {
|
||||||
|
public get gameHost(): GameHost<TState, TResult, TModule> {
|
||||||
|
const gameHost = this.initData.gameHost;
|
||||||
if (!gameHost) {
|
if (!gameHost) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`GameHostScene (${this.scene.key}): gameHost 未提供。` +
|
`GameHostScene (${this.scene.key}): gameHost 未提供。` +
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import Phaser from "phaser";
|
||||||
import { GameHostScene } from "boardgame-phaser";
|
import { GameHostScene } from "boardgame-phaser";
|
||||||
import { spawnEffect, type Spawner } from "boardgame-phaser";
|
import { spawnEffect, type Spawner } from "boardgame-phaser";
|
||||||
import {
|
import {
|
||||||
|
CombatResult,
|
||||||
type CombatState,
|
type CombatState,
|
||||||
prompts,
|
prompts,
|
||||||
} from "boardgame-core/samples/slay-the-spire-like";
|
} from "boardgame-core/samples/slay-the-spire-like";
|
||||||
|
|
@ -12,12 +13,17 @@ import {
|
||||||
type CombatUnitData,
|
type CombatUnitData,
|
||||||
} from "@/gameobjects/CombatUnitContainer";
|
} from "@/gameobjects/CombatUnitContainer";
|
||||||
import { CardSpawner } from "@/gameobjects/CardSpawner";
|
import { CardSpawner } from "@/gameobjects/CardSpawner";
|
||||||
|
import { CombatModule } from "@/state/combatState";
|
||||||
|
|
||||||
const CARD_SPACING = 160;
|
const CARD_SPACING = 160;
|
||||||
const HAND_MARGIN = 100;
|
const HAND_MARGIN = 100;
|
||||||
const HAND_Y = 140;
|
const HAND_Y = 140;
|
||||||
|
|
||||||
export class CombatTestScene extends GameHostScene<CombatState> {
|
export class CombatTestScene extends GameHostScene<
|
||||||
|
CombatState,
|
||||||
|
CombatResult | null,
|
||||||
|
CombatModule
|
||||||
|
> {
|
||||||
private selectedCardId: string | null = null;
|
private selectedCardId: string | null = null;
|
||||||
private isTargeting = false;
|
private isTargeting = false;
|
||||||
private targetingText!: Phaser.GameObjects.Text;
|
private targetingText!: Phaser.GameObjects.Text;
|
||||||
|
|
@ -150,8 +156,9 @@ export class CombatTestScene extends GameHostScene<CombatState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private tryPlayCard(cardId: string, targetId?: string): void {
|
private tryPlayCard(cardId: string, targetId?: string): void {
|
||||||
const error = this.gameHost.tryAnswerPrompt(
|
const error = this.gameHost.prompts.tryCommit(
|
||||||
prompts.mainAction,
|
prompts.mainAction,
|
||||||
|
"player",
|
||||||
cardId,
|
cardId,
|
||||||
targetId,
|
targetId,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import {
|
import {
|
||||||
buildCombatState,
|
buildCombatState,
|
||||||
CombatState,
|
|
||||||
createRunState,
|
createRunState,
|
||||||
createStartWith,
|
createStart,
|
||||||
|
createTriggers,
|
||||||
data,
|
data,
|
||||||
generateDeckFromInventory,
|
generateDeckFromInventory,
|
||||||
getAdjacentItems,
|
getAdjacentItems,
|
||||||
|
|
@ -10,9 +10,9 @@ import {
|
||||||
IRunContext,
|
IRunContext,
|
||||||
} from "boardgame-core/samples/slay-the-spire-like";
|
} from "boardgame-core/samples/slay-the-spire-like";
|
||||||
import { createInventorySignal } from "./inventory";
|
import { createInventorySignal } from "./inventory";
|
||||||
import { GameModule } from "boardgame-core";
|
|
||||||
|
|
||||||
export function createCombatState() {
|
export type CombatModule = ReturnType<typeof createCombatModule>;
|
||||||
|
export function createCombatModule() {
|
||||||
const inventory = createInventorySignal(true);
|
const inventory = createInventorySignal(true);
|
||||||
|
|
||||||
const deck = generateDeckFromInventory(inventory.value);
|
const deck = generateDeckFromInventory(inventory.value);
|
||||||
|
|
@ -51,12 +51,13 @@ export function createCombatState() {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const start = createStartWith((triggers, ctx) => {
|
const triggers = createTriggers(runContext);
|
||||||
data.desert.addTriggers(triggers, runContext);
|
const start = createStart(triggers, runContext);
|
||||||
}, runContext);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
start,
|
start,
|
||||||
createInitialState: () => combat,
|
createInitialState: () => combat,
|
||||||
} as GameModule<CombatState>;
|
triggers,
|
||||||
|
runContext,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,25 @@
|
||||||
import { createGameHost, type GameModule } from "boardgame-core";
|
import { GAME_CONFIG } from "@/config";
|
||||||
|
import { CombatTestScene } from "@/scenes/CombatTestScene";
|
||||||
|
import { GridViewerScene } from "@/scenes/GridViewerScene";
|
||||||
|
import { IndexScene } from "@/scenes/IndexScene";
|
||||||
|
import { InventoryTestScene } from "@/scenes/InventoryTestScene";
|
||||||
|
import { MapViewerScene } from "@/scenes/MapViewerScene";
|
||||||
|
import { ShapeViewerScene } from "@/scenes/ShapeViewerScene";
|
||||||
|
import { CombatModule, createCombatModule } from "@/state/combatState";
|
||||||
|
import { createGameHost } from "boardgame-core";
|
||||||
|
import {
|
||||||
|
CombatResult,
|
||||||
|
CombatState,
|
||||||
|
} from "boardgame-core/samples/slay-the-spire-like";
|
||||||
import { PhaserGame, PhaserScene } from "boardgame-phaser";
|
import { PhaserGame, PhaserScene } from "boardgame-phaser";
|
||||||
import { useMemo } from "preact/hooks";
|
import { useMemo } from "preact/hooks";
|
||||||
import { IndexScene } from "@/scenes/IndexScene";
|
|
||||||
import { MapViewerScene } from "@/scenes/MapViewerScene";
|
|
||||||
import { GridViewerScene } from "@/scenes/GridViewerScene";
|
|
||||||
import { ShapeViewerScene } from "@/scenes/ShapeViewerScene";
|
|
||||||
import { GAME_CONFIG } from "@/config";
|
|
||||||
import { InventoryTestScene } from "@/scenes/InventoryTestScene";
|
|
||||||
import { CombatTestScene } from "@/scenes/CombatTestScene";
|
|
||||||
import { createCombatState } from "@/state/combatState";
|
|
||||||
import type { CombatState } from "boardgame-core/samples/slay-the-spire-like";
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const gameHost = useMemo(
|
const gameHost = useMemo(
|
||||||
() => createGameHost(createCombatState() as GameModule<CombatState>),
|
() =>
|
||||||
|
createGameHost<CombatState, CombatResult | null, CombatModule>(
|
||||||
|
createCombatModule(),
|
||||||
|
),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue