4b7aa82953
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m18s
- Add new golem component types (Core, Frame, MindCircuit, Enchantment) - Create 4 Core tiers, 7 Frames, 4 Mind Circuits, 8 Enchantments - Rewrite golem utils for component-based stat computation - Update GolemancyState with new fields (golemDesigns, golemLoadout, activeGolems) - Update combat store, actions, and pipelines for new golem system - Rewrite GolemancyTab with component selection UI - Update fabricator discipline perks for new system - Add comprehensive tests for component registries and utilities - All files under 400 lines, all 743 tests passing
121 lines
4.1 KiB
TypeScript
121 lines
4.1 KiB
TypeScript
// ─── Golem Combat Pipeline (Component-Based) ─────────────────────────────────
|
|
// Pipeline integration for the component-based golem combat system.
|
|
// Extracts golem combat setup from gameStore.ts tick() to keep the coordinator
|
|
// under the 400-line file limit.
|
|
|
|
import { useCombatStore } from '../combatStore';
|
|
import { useManaStore } from '../manaStore';
|
|
import {
|
|
summonGolemsOnRoomEntry,
|
|
processGolemMaintenance,
|
|
processGolemManaRegen,
|
|
processGolemAttacks,
|
|
countdownGolemRoomDuration,
|
|
} from '../golem-combat-actions';
|
|
import { useAttunementStore } from '../attunementStore';
|
|
import type { RuntimeActiveGolem } from '../../types';
|
|
|
|
export interface GolemCombatContext {
|
|
addLog: (msg: string) => void;
|
|
ctx: {
|
|
combat: {
|
|
currentFloor: number;
|
|
currentRoom: { roomType: string; enemies: Array<{ name: string; hp: number; maxHP: number; armor: number }> };
|
|
};
|
|
prestige: { signedPacts: number[] };
|
|
};
|
|
rawMana: number;
|
|
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
|
|
maxMana: number;
|
|
}
|
|
|
|
export interface GolemCombatPipelineResult {
|
|
rawMana: number;
|
|
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
|
|
activeGolems: RuntimeActiveGolem[];
|
|
logMessages: string[];
|
|
}
|
|
|
|
/**
|
|
* Build the golem combat pipeline for the current tick.
|
|
*/
|
|
export function buildGolemCombatPipeline(_addLog: (msg: string) => void): {
|
|
activeGolems: RuntimeActiveGolem[];
|
|
golemDesigns: Record<string, { id: string; name: string; coreId: string; frameId: string; mindCircuitId: string; enchantmentIds: string[]; selectedManaTypes: string[]; selectedSpells: string[] }>;
|
|
golemApplyDamageToRoom: (dmg: number) => { floorHP: number; floorMaxHP: number; roomCleared: boolean };
|
|
} {
|
|
const combatState = useCombatStore.getState();
|
|
const golemancy = combatState.golemancy;
|
|
|
|
// New component-based active golems
|
|
const activeGolems = golemancy?.activeGolems ?? [];
|
|
|
|
// Reconstruct golem designs from store
|
|
const golemDesigns = golemancy?.golemDesigns ?? {};
|
|
|
|
const golemApplyDamageToRoom = (dmg: number) => {
|
|
const cs = useCombatStore.getState();
|
|
const room = cs.currentRoom;
|
|
if (!room || room.enemies.length === 0 || dmg <= 0) {
|
|
return { floorHP: cs.floorHP, floorMaxHP: cs.floorMaxHP, roomCleared: false };
|
|
}
|
|
|
|
// Focus-fire targeting: target lowest HP enemy
|
|
let target = room.enemies[0];
|
|
for (const e of room.enemies) {
|
|
if (e.hp > 0 && e.hp < (target?.hp ?? Infinity)) {
|
|
target = e;
|
|
}
|
|
}
|
|
if (!target || target.hp <= 0) {
|
|
return { floorHP: cs.floorHP, floorMaxHP: cs.floorMaxHP, roomCleared: false };
|
|
}
|
|
|
|
const updatedEnemies = room.enemies.map((enemy) => {
|
|
if (enemy.id === target!.id && enemy.hp > 0) {
|
|
return { ...enemy, hp: Math.max(0, enemy.hp - dmg) };
|
|
}
|
|
return enemy;
|
|
});
|
|
|
|
const newFloorHP = updatedEnemies.reduce((sum, e) => sum + e.hp, 0);
|
|
const allDead = updatedEnemies.every((e) => e.hp <= 0);
|
|
|
|
useCombatStore.setState({
|
|
currentRoom: { ...room, enemies: updatedEnemies },
|
|
floorHP: newFloorHP,
|
|
});
|
|
|
|
return { floorHP: newFloorHP, floorMaxHP: cs.floorMaxHP, roomCleared: allDead };
|
|
};
|
|
|
|
return { activeGolems, golemDesigns, golemApplyDamageToRoom };
|
|
}
|
|
|
|
/**
|
|
* Process golem summoning on room entry.
|
|
*/
|
|
export function processGolemRoomEntry(
|
|
loadout: { enabled: boolean; designId: string; design: { name: string } }[],
|
|
currentFloor: number,
|
|
): {
|
|
rawMana: number;
|
|
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
|
|
activeGolems: RuntimeActiveGolem[];
|
|
logMessages: string[];
|
|
} {
|
|
const cs = useCombatStore.getState();
|
|
const attStore = useAttunementStore.getState();
|
|
const fabLevel = attStore.attunements?.fabricator?.level ?? 0;
|
|
const discBonus = 0; // TODO: compute from discipline
|
|
|
|
return summonGolemsOnRoomEntry(
|
|
loadout as any,
|
|
useManaStore.getState().rawMana,
|
|
useManaStore.getState().elements as any,
|
|
currentFloor,
|
|
cs.golemancy.activeGolems as any[],
|
|
discBonus,
|
|
);
|
|
}
|