refactor: tick pipeline pattern — read all → compute all → write all (issue #103)
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
- New tick-pipeline.ts: TickContext/TickWrites types + buildTickContext/applyTickWrites orchestrator
- gameStore.ts tick(): refactored to 3-phase pipeline (read snapshot → compute updates → batch writes)
- combat-actions.ts: accept signedPacts as parameter instead of usePrestigeStore.getState() in combat loop
- combatStore.ts/combat-state.types.ts: updated processCombatTick signature for signedPacts passthrough
- craftingStore.ts: removed tempState = { ...get(), rawMana } as any anti-pattern
- preparation-actions.ts: accept rawMana as explicit parameter instead of GameState bag
This commit is contained in:
@@ -1,13 +1,25 @@
|
||||
// ─── Combat Actions ─────────────────────────────────────────────────────────────
|
||||
// Extracted combat logic from combatStore.ts
|
||||
// Pure combat logic — no cross-store getState() calls.
|
||||
// All external data (signedPacts, etc.) is passed in as parameters.
|
||||
|
||||
import { SPELLS_DEF, GUARDIANS, HOURS_PER_TICK } from '../constants';
|
||||
import type { CombatState } from './combat-state.types';
|
||||
import type { SpellState } from '../types';
|
||||
import { getFloorMaxHP, getFloorElement, calcDamage, canAffordSpellCost, deductSpellCost } from '../utils';
|
||||
import { usePrestigeStore } from './prestigeStore';
|
||||
import { computeDisciplineEffects } from '../effects/discipline-effects';
|
||||
import { useDisciplineStore } from './discipline-slice';
|
||||
|
||||
export interface CombatTickResult {
|
||||
rawMana: number;
|
||||
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
|
||||
logMessages: string[];
|
||||
totalManaGathered: number;
|
||||
currentFloor: number;
|
||||
floorHP: number;
|
||||
floorMaxHP: number;
|
||||
maxFloorReached: number;
|
||||
castProgress: number;
|
||||
equipmentSpellStates: CombatState['equipmentSpellStates'];
|
||||
}
|
||||
|
||||
export function processCombatTick(
|
||||
get: () => CombatState,
|
||||
@@ -22,19 +34,42 @@ export function processCombatTick(
|
||||
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
|
||||
modifiedDamage?: number;
|
||||
},
|
||||
) {
|
||||
signedPacts: number[],
|
||||
): CombatTickResult {
|
||||
const state = get();
|
||||
const logMessages: string[] = [];
|
||||
let totalManaGathered = 0;
|
||||
|
||||
if (state.currentAction !== 'climb') {
|
||||
return { rawMana, elements, logMessages, totalManaGathered };
|
||||
return {
|
||||
rawMana,
|
||||
elements,
|
||||
logMessages,
|
||||
totalManaGathered,
|
||||
currentFloor: state.currentFloor,
|
||||
floorHP: state.floorHP,
|
||||
floorMaxHP: state.floorMaxHP,
|
||||
maxFloorReached: state.maxFloorReached,
|
||||
castProgress: state.castProgress,
|
||||
equipmentSpellStates: state.equipmentSpellStates,
|
||||
};
|
||||
}
|
||||
|
||||
const spellId = state.activeSpell;
|
||||
const spellDef = SPELLS_DEF[spellId];
|
||||
if (!spellDef) {
|
||||
return { rawMana, elements, logMessages, totalManaGathered };
|
||||
return {
|
||||
rawMana,
|
||||
elements,
|
||||
logMessages,
|
||||
totalManaGathered,
|
||||
currentFloor: state.currentFloor,
|
||||
floorHP: state.floorHP,
|
||||
floorMaxHP: state.floorMaxHP,
|
||||
maxFloorReached: state.maxFloorReached,
|
||||
castProgress: state.castProgress,
|
||||
equipmentSpellStates: state.equipmentSpellStates,
|
||||
};
|
||||
}
|
||||
|
||||
// Compute discipline bonuses once per tick
|
||||
@@ -59,7 +94,7 @@ export function processCombatTick(
|
||||
// Calculate base damage
|
||||
const floorElement = getFloorElement(currentFloor);
|
||||
const damage = calcDamage(
|
||||
{ skills: {}, signedPacts: usePrestigeStore.getState().signedPacts },
|
||||
{ skills: {}, signedPacts },
|
||||
spellId,
|
||||
floorElement,
|
||||
disciplineEffects,
|
||||
@@ -114,7 +149,7 @@ export function processCombatTick(
|
||||
// Calculate damage
|
||||
const eFloorElement = getFloorElement(currentFloor);
|
||||
const eDamage = calcDamage(
|
||||
{ skills: {}, signedPacts: usePrestigeStore.getState().signedPacts },
|
||||
{ skills: {}, signedPacts },
|
||||
eSpell.spellId,
|
||||
eFloorElement,
|
||||
disciplineEffects,
|
||||
@@ -135,16 +170,29 @@ export function processCombatTick(
|
||||
updatedEquipmentSpellStates[i] = { ...eSpell, castProgress: eCastProgress % 1 };
|
||||
}
|
||||
|
||||
const newMaxFloorReached = Math.max(state.maxFloorReached, currentFloor);
|
||||
|
||||
set({
|
||||
currentFloor,
|
||||
floorHP,
|
||||
floorMaxHP: getFloorMaxHP(currentFloor),
|
||||
maxFloorReached: Math.max(state.maxFloorReached, currentFloor),
|
||||
maxFloorReached: newMaxFloorReached,
|
||||
castProgress,
|
||||
equipmentSpellStates: updatedEquipmentSpellStates,
|
||||
});
|
||||
|
||||
return { rawMana, elements, logMessages, totalManaGathered };
|
||||
return {
|
||||
rawMana,
|
||||
elements,
|
||||
logMessages,
|
||||
totalManaGathered,
|
||||
currentFloor,
|
||||
floorHP,
|
||||
floorMaxHP: getFloorMaxHP(currentFloor),
|
||||
maxFloorReached: newMaxFloorReached,
|
||||
castProgress,
|
||||
equipmentSpellStates: updatedEquipmentSpellStates,
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function to create initial spells
|
||||
|
||||
Reference in New Issue
Block a user