feat: implement golemancy combat system (spec §6, §9)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s
- Add ActiveGolem interface and activeGolems to GolemancyState - Add maxRoomDuration to all 12 golem definitions - Create golem-combat-actions.ts with pure golem combat logic (summoning, maintenance, attacks, room-duration) - Create golem-combat.ts pipeline for golem combat setup - Wire golem maintenance and attacks into processCombatTick - Wire golem summoning into advanceRoomOrFloor on room entry - Wire golem room-duration countdown into onFloorCleared callback - Update combat-actions tests for new processCombatTick signature - All 921 tests pass, all files under 400-line limit Closes #259
This commit is contained in:
@@ -36,7 +36,7 @@ function resetStores() {
|
||||
roomResetState: {},
|
||||
clearedRooms: {},
|
||||
isDescentComplete: false,
|
||||
golemancy: { enabledGolems: [], summonedGolems: [], lastSummonFloor: 0 },
|
||||
golemancy: { enabledGolems: [], summonedGolems: [], activeGolems: [], lastSummonFloor: 0 },
|
||||
equipmentSpellStates: [],
|
||||
comboHitCount: 0,
|
||||
floorHitCount: 0,
|
||||
@@ -56,6 +56,12 @@ function resetStores() {
|
||||
useDisciplineStore.setState({ disciplines: {}, activeIds: [], concurrentLimit: 1, totalXP: 0, processedPerks: [] });
|
||||
}
|
||||
|
||||
function golemApplyDamageToRoom(dmg: number): { floorHP: number; floorMaxHP: number; roomCleared: boolean } {
|
||||
const cs = useCombatStore.getState();
|
||||
const newFloorHP = Math.max(0, cs.floorHP - dmg);
|
||||
return { floorHP: newFloorHP, floorMaxHP: cs.floorMaxHP, roomCleared: newFloorHP <= 0 };
|
||||
}
|
||||
|
||||
function runCombatTick(rawMana: number, elements: Record<string, { current: number; max: number; unlocked: boolean }>): CombatTickResult {
|
||||
return processCombatTick(
|
||||
() => useCombatStore.getState(),
|
||||
@@ -67,6 +73,27 @@ function runCombatTick(rawMana: number, elements: Record<string, { current: numb
|
||||
vi.fn(), // onFloorCleared
|
||||
(dmg) => ({ rawMana, elements, modifiedDamage: dmg }), // onDamageDealt (no modifiers)
|
||||
[], // signedPacts
|
||||
{ activeGolems: [] }, // golemancyState
|
||||
golemApplyDamageToRoom,
|
||||
);
|
||||
}
|
||||
|
||||
function processCombatTickDirect(
|
||||
get: () => ReturnType<typeof useCombatStore.getState>,
|
||||
set: (partial: Record<string, unknown>) => void,
|
||||
rawMana: number,
|
||||
elements: Record<string, { current: number; max: number; unlocked: boolean }>,
|
||||
maxMana: number,
|
||||
attackSpeedMult: number,
|
||||
onFloorCleared: (floor: number, wasGuardian: boolean) => void,
|
||||
onDamageDealt: (dmg: number) => { rawMana: number; elements: Record<string, { current: number; max: number; unlocked: boolean }>; modifiedDamage?: number },
|
||||
signedPacts: number[],
|
||||
): CombatTickResult {
|
||||
return processCombatTick(
|
||||
get, set, rawMana, elements, maxMana, attackSpeedMult,
|
||||
onFloorCleared, onDamageDealt, signedPacts,
|
||||
{ activeGolems: [] },
|
||||
golemApplyDamageToRoom,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -160,7 +187,7 @@ describe('processCombatTick', () => {
|
||||
const elements = makeInitialElements(500, {});
|
||||
const state = useCombatStore.getState();
|
||||
// With 0 mana and 0 progress, no cast should complete
|
||||
const result = processCombatTick(
|
||||
const result = processCombatTickDirect(
|
||||
() => state,
|
||||
() => {},
|
||||
0, // no mana
|
||||
@@ -196,7 +223,7 @@ describe('processCombatTick', () => {
|
||||
// path that acts as a safety net.
|
||||
useCombatStore.setState({ activeSpell: 'totallyInvalidSpell' });
|
||||
const elements = makeInitialElements(500, {});
|
||||
const result = processCombatTick(
|
||||
const result = processCombatTickDirect(
|
||||
() => useCombatStore.getState(),
|
||||
() => {},
|
||||
1000,
|
||||
@@ -218,7 +245,7 @@ describe('processCombatTick', () => {
|
||||
const elements = makeInitialElements(500, {});
|
||||
useCombatStore.setState({ castProgress: 0.99 });
|
||||
expect(() => {
|
||||
processCombatTick(
|
||||
processCombatTickDirect(
|
||||
() => useCombatStore.getState(),
|
||||
() => {},
|
||||
1000,
|
||||
|
||||
Reference in New Issue
Block a user