refactor: simplify buildCombatState and fix formatting in encounter

system

Refactor `buildCombatState` to derive encounter data directly from
`runState` instead of requiring it as an argument. Also apply
consistent 2-space indentation and formatting to the encounter
lifecycle module.
This commit is contained in:
hypercross 2026-04-19 15:42:47 +08:00
parent 89d96d838b
commit 8142fbfa60
1 changed files with 151 additions and 139 deletions

View File

@ -1,9 +1,19 @@
import type { PointCrawlMap } from '../map/types'; import type { PointCrawlMap } from "../map/types";
import type { CombatState, EnemyEntity, PlayerEntity, EffectTable } from '../combat/types'; import type {
import type { EncounterData, EnemyData, EffectData, IntentData } from '../types'; CombatState,
import type { RunState } from './types'; EnemyEntity,
import { generateDeckFromInventory } from '../deck/factory'; PlayerEntity,
import { ReadonlyRNG } from '@/utils/rng'; EffectTable,
} from "../combat/types";
import type {
EncounterData,
EnemyData,
EffectData,
IntentData,
} from "../types";
import type { RunState } from "./types";
import { generateDeckFromInventory } from "../deck/factory";
import { ReadonlyRNG } from "@/utils/rng";
// -- Encounter assignment to nodes -- // -- Encounter assignment to nodes --
@ -14,7 +24,7 @@ import { ReadonlyRNG } from '@/utils/rng';
export function assignEncounterToNode( export function assignEncounterToNode(
map: PointCrawlMap, map: PointCrawlMap,
nodeId: string, nodeId: string,
encounter: EncounterData encounter: EncounterData,
): void { ): void {
const node = map.nodes.get(nodeId); const node = map.nodes.get(nodeId);
if (!node) { if (!node) {
@ -30,12 +40,12 @@ export function assignEncounterToNode(
export function assignEncountersFromPool( export function assignEncountersFromPool(
map: PointCrawlMap, map: PointCrawlMap,
encounterPool: EncounterData[], encounterPool: EncounterData[],
rng: ReadonlyRNG rng: ReadonlyRNG,
): void { ): void {
if (encounterPool.length === 0) return; if (encounterPool.length === 0) return;
for (const node of map.nodes.values()) { for (const node of map.nodes.values()) {
if (node.type === 'start' || node.type === 'end') continue; if (node.type === "start" || node.type === "end") continue;
if (node.encounter) continue; if (node.encounter) continue;
const assigned = encounterPool[rng.nextInt(encounterPool.length)]; const assigned = encounterPool[rng.nextInt(encounterPool.length)];
@ -50,10 +60,10 @@ export function assignEncountersFromPool(
export function assignAllEncounters( export function assignAllEncounters(
map: PointCrawlMap, map: PointCrawlMap,
encounterIndex: Map<string, EncounterData[]>, encounterIndex: Map<string, EncounterData[]>,
rng: ReadonlyRNG rng: ReadonlyRNG,
): void { ): void {
for (const node of map.nodes.values()) { for (const node of map.nodes.values()) {
if (node.type === 'start' || node.type === 'end') continue; if (node.type === "start" || node.type === "end") continue;
if (node.encounter) continue; if (node.encounter) continue;
const encounterType = node.type; const encounterType = node.type;
@ -72,10 +82,11 @@ export function assignAllEncounters(
* - Creates PlayerEntity with energy (3), deck from inventory, and HP from run state * - Creates PlayerEntity with energy (3), deck from inventory, and HP from run state
* - Sets initial phase to 'playerTurn', turn 1 * - Sets initial phase to 'playerTurn', turn 1
*/ */
export function buildCombatState( export function buildCombatState(runState: RunState): CombatState {
encounter: EncounterData, const encounter = getCurrentEncounterData(runState);
runState: RunState, if (!encounter)
): CombatState { throw new Error(`No encounter found for node ${runState.currentNodeId}`);
const enemies = createEnemyEntities(encounter); const enemies = createEnemyEntities(encounter);
const deck = generateDeckFromInventory(runState.inventory); const deck = generateDeckFromInventory(runState.inventory);
const player = createPlayerEntity(runState, deck); const player = createPlayerEntity(runState, deck);
@ -84,7 +95,7 @@ export function buildCombatState(
enemies, enemies,
player, player,
inventory: runState.inventory, inventory: runState.inventory,
phase: 'playerTurn', phase: "playerTurn",
turnNumber: 1, turnNumber: 1,
result: null, result: null,
loot: [], loot: [],
@ -95,9 +106,7 @@ export function buildCombatState(
* Creates EnemyEntity instances from encounter enemy definitions. * Creates EnemyEntity instances from encounter enemy definitions.
* Each enemy gets: HP from encounter tuple, initial buffs from encounter, intents from enemy definition. * Each enemy gets: HP from encounter tuple, initial buffs from encounter, intents from enemy definition.
*/ */
export function createEnemyEntities( export function createEnemyEntities(encounter: EncounterData): EnemyEntity[] {
encounter: EncounterData,
): EnemyEntity[] {
const enemies: EnemyEntity[] = []; const enemies: EnemyEntity[] = [];
let instanceCounter = 0; let instanceCounter = 0;
@ -126,9 +135,7 @@ export function createEnemyEntities(
/** /**
* Builds a map of intent ID -> IntentData for an enemy. * Builds a map of intent ID -> IntentData for an enemy.
*/ */
function buildIntentMap( function buildIntentMap(enemy: EnemyData): Record<string, IntentData> {
enemy: EnemyData,
): Record<string, IntentData> {
const intents: Record<string, IntentData> = {}; const intents: Record<string, IntentData> = {};
for (const intent of enemy.intents) { for (const intent of enemy.intents) {
intents[intent.id] = intent; intents[intent.id] = intent;
@ -165,7 +172,10 @@ function buildEffectTable(buffs: readonly [EffectData, number][]): EffectTable {
/** /**
* Creates a PlayerEntity from the run state and deck. * Creates a PlayerEntity from the run state and deck.
*/ */
function createPlayerEntity(runState: RunState, deck: ReturnType<typeof generateDeckFromInventory>): PlayerEntity { function createPlayerEntity(
runState: RunState,
deck: ReturnType<typeof generateDeckFromInventory>,
): PlayerEntity {
return { return {
id: "player", id: "player",
hp: runState.player.currentHp, hp: runState.player.currentHp,
@ -184,7 +194,9 @@ function createPlayerEntity(runState: RunState, deck: ReturnType<typeof generate
/** /**
* Gets the encounter data for the current node. * Gets the encounter data for the current node.
*/ */
export function getCurrentEncounterData(runState: RunState): EncounterData | undefined { export function getCurrentEncounterData(
runState: RunState,
): EncounterData | undefined {
const node = runState.map.nodes.get(runState.currentNodeId); const node = runState.map.nodes.get(runState.currentNodeId);
return node?.encounter; return node?.encounter;
} }
@ -207,7 +219,7 @@ export function startEncounter(runState: RunState): CombatState | null {
return null; return null;
} }
return buildCombatState(encounter, runState); return buildCombatState(runState);
} }
/** /**
@ -217,10 +229,10 @@ export function startEncounter(runState: RunState): CombatState | null {
*/ */
export function resolveCombatEncounter( export function resolveCombatEncounter(
runState: RunState, runState: RunState,
combatState: CombatState combatState: CombatState,
): { success: true } | { success: false; reason: string } { ): { success: true } | { success: false; reason: string } {
if (runState.currentEncounter.resolved) { if (runState.currentEncounter.resolved) {
return { success: false, reason: '该遭遇已解决' }; return { success: false, reason: "该遭遇已解决" };
} }
// Apply HP from combat state back to run state // Apply HP from combat state back to run state
@ -228,7 +240,7 @@ export function resolveCombatEncounter(
// Apply loot // Apply loot
for (const loot of combatState.loot) { for (const loot of combatState.loot) {
if (loot.type === 'gold') { if (loot.type === "gold") {
runState.player.gold += loot.amount; runState.player.gold += loot.amount;
} }
// Item rewards are handled by the caller via addItem() // Item rewards are handled by the caller via addItem()