import { ReactiveScene } from "boardgame-phaser"; import { spawnEffect, type Spawner } from "boardgame-phaser"; import { mutableSignal } from "boardgame-core"; import { type CombatState } from "boardgame-core/samples/slay-the-spire-like"; import { createCombatState } from "@/state/combatState"; import { createButton } from "@/utils/createButton"; import { SceneKey } from "./types"; import { CombatUnitContainer, type CombatUnitData, } from "@/gameobjects/CombatUnitContainer"; export class CombatTestScene extends ReactiveScene { private combatSignal = mutableSignal(null); constructor() { super("CombatTestScene"); } create(): void { super.create(); const { width, height } = this.scale; const module = createCombatState(); this.combatSignal.value = module.createInitialState(); this.add .text(width / 2, 30, "Combat State Test", { fontSize: "24px", color: "#ffffff", fontStyle: "bold", }) .setOrigin(0.5); this.add .text(width / 2, 60, "Player & Enemies with Buffs / HP", { fontSize: "14px", color: "#aaaaaa", }) .setOrigin(0.5); const unitSpawner: Spawner = { getData: () => this.generateUnitData(), getKey: (t) => t.key, onSpawn: (t) => this.spawnUnit(t, width, height), onUpdate: (t, obj) => obj.updateFromData(t), onDespawn: (obj) => obj.destroy(), }; const disposeSpawner = spawnEffect(unitSpawner); this.disposables.add(disposeSpawner); this.createControls(width, height); } private generateUnitData(): CombatUnitData[] { const combat = this.combatSignal.value; if (!combat) return []; const units: CombatUnitData[] = [ { key: "player", entity: combat.player, name: "Player", isPlayer: true, }, ]; combat.enemies.forEach((enemy, i) => { units.push({ key: `enemy-${i}`, entity: enemy, name: enemy.enemy.name, isPlayer: false, }); }); return units; } private spawnUnit( t: CombatUnitData, width: number, height: number, ): CombatUnitContainer | null { const combat = this.combatSignal.value; if (!combat) return null; const totalUnits = 1 + combat.enemies.length; const spacing = 220; const totalWidth = (totalUnits - 1) * spacing; const startX = width / 2 - totalWidth / 2; let x = startX; if (t.key.startsWith("enemy-")) { const index = parseInt(t.key.replace("enemy-", ""), 10); x = startX + (index + 1) * spacing; } const container = new CombatUnitContainer(this, x, height / 2, t); container.playSpawnEffect(); return container; } private createControls(width: number, height: number): void { createButton({ scene: this, label: "θΏ”ε›žθœε•", x: 100, y: 40, onClick: async () => { await this.sceneController.launch(SceneKey.IndexScene); }, }); createButton({ scene: this, label: "Damage Player", x: width - 520, y: height - 40, onClick: () => { this.combatSignal.produce((draft) => { if (!draft) return; draft.player.hp = Math.max(0, draft.player.hp - 5); if (draft.player.hp <= 0) draft.player.isAlive = false; }); }, }); createButton({ scene: this, label: "Damage Enemy", x: width - 380, y: height - 40, onClick: () => { this.combatSignal.produce((draft) => { if (!draft) return; const enemy = draft.enemies[0]; if (enemy) { enemy.hp = Math.max(0, enemy.hp - 5); if (enemy.hp <= 0) enemy.isAlive = false; } }); }, }); createButton({ scene: this, label: "Heal Player", x: width - 240, y: height - 40, onClick: () => { this.combatSignal.produce((draft) => { if (!draft) return; draft.player.hp = Math.min(draft.player.maxHp, draft.player.hp + 5); if (draft.player.hp > 0) draft.player.isAlive = true; }); }, }); createButton({ scene: this, label: "Add Buff", x: width - 100, y: height - 40, onClick: () => { this.combatSignal.produce((draft) => { if (!draft) return; const current = draft.player.effects["strength"]; draft.player.effects["strength"] = { data: { id: "strength", name: "Strength", description: "Deal +1 damage per stack", lifecycle: "temporary", }, stacks: (current?.stacks ?? 0) + 1, }; }); }, }); } }