boardgame-core/src/samples/slay-the-spire-like/system/encounter/combat.ts

114 lines
2.8 KiB
TypeScript

import {
CombatState,
EffectTable,
EnemyEntity,
PlayerEntity,
} from "../combat/types";
import { generateDeckFromInventory } from "../deck";
import { GridInventory } from "../grid-inventory";
import { GameItemMeta } from "../grid-inventory/types";
import { EffectData, EncounterData, EnemyData, IntentData } from "../types";
import { CombatEncounterState, RunState } from "./types";
export function buildCombatState(
runState: RunState,
inventory: GridInventory<GameItemMeta>,
encounter: CombatEncounterState,
): CombatState {
const deck = generateDeckFromInventory(inventory);
const player = createPlayerEntity(runState, deck);
const enemies = createEnemyEntities(encounter.data);
return {
enemies,
player,
phase: "playerTurn",
turnNumber: 1,
result: null,
loot: [],
};
}
export function buildCombatEncounterState(
data: EncounterData<"minion" | "elite">,
): CombatEncounterState {
return { data, blocked: false };
}
function createEnemyEntities(encounter: EncounterData): EnemyEntity[] {
const enemies: EnemyEntity[] = [];
let instanceCounter = 0;
for (const [enemyData, hp, encounterBuffs] of encounter.enemies) {
const instanceId = `${enemyData.id}-${instanceCounter++}`;
const intents = buildIntentMap(enemyData);
const initialIntent = findInitialIntent(enemyData);
const effects = buildEffectTable(encounterBuffs);
const entity: EnemyEntity = {
id: instanceId,
enemy: enemyData,
hp,
maxHp: hp,
isAlive: true,
effects,
intents,
currentIntent: initialIntent,
};
enemies.push(entity);
}
return enemies;
}
/**
* Builds a map of intent ID -> IntentData for an enemy.
*/
function buildIntentMap(enemy: EnemyData): Record<string, IntentData> {
const intents: Record<string, IntentData> = {};
for (const intent of enemy.intents) {
intents[intent.id] = intent;
}
return intents;
}
/**
* Finds the initial intent ID for an enemy.
*/
function findInitialIntent(enemy: EnemyData): IntentData {
for (const intent of enemy.intents) {
if (intent.initialIntent) {
return intent;
}
}
if (enemy.intents.length === 0) {
throw new Error(`Enemy "${enemy.id}" has no intents`);
}
return enemy.intents[0];
}
function buildEffectTable(buffs: readonly [EffectData, number][]): EffectTable {
const table: EffectTable = {};
for (const [effect, stacks] of buffs) {
table[effect.id] = { data: effect, stacks };
}
return table;
}
function createPlayerEntity(
runState: RunState,
deck: ReturnType<typeof generateDeckFromInventory>,
): PlayerEntity {
return {
id: "player",
hp: runState.currentHp,
maxHp: runState.maxHp,
isAlive: runState.currentHp > 0,
energy: 3,
maxEnergy: 3,
deck,
itemEffects: {},
effects: {},
};
}