feat: Implement critical special effects (partial)
Some checks failed
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m9s

- Add consecutiveHits to GameState for battle effects
- Implement MANA_ECHO (10% double mana on click)
- Implement EMERGENCY_RESERVE (keep 10% mana on new loop)
- Add foundation for BATTLE_FURY and COMBO_MASTER
- Add lifesteal and spell echo from equipment
- Add parallel study processing in tick
This commit is contained in:
2026-03-26 13:24:04 +00:00
parent 315490cedb
commit 751b317af2
2 changed files with 53 additions and 7 deletions

View File

@@ -22,7 +22,7 @@ import {
BASE_UNLOCKED_EFFECTS,
ENCHANTING_UNLOCK_EFFECTS,
} from './constants';
import { hasSpecial, SPECIAL_EFFECTS } from './upgrade-effects';
import { hasSpecial, SPECIAL_EFFECTS, computeDynamicRegen } from './upgrade-effects';
import { getUnifiedEffects } from './effects';
import { SKILL_EVOLUTION_PATHS } from './skill-evolution';
import {
@@ -206,6 +206,9 @@ function makeInitial(overrides: Partial<GameState> = {}): GameState {
skillUpgrades: overrides.skillUpgrades || {},
skillTiers: overrides.skillTiers || {},
parallelStudyTarget: null,
studyStartedAt: null,
consecutiveStudyHours: 0,
lastStudyCost: 0,
// New equipment system
equippedInstances: startingEquipment.equippedInstances,
@@ -256,6 +259,7 @@ function makeInitial(overrides: Partial<GameState> = {}): GameState {
elementChain: [],
},
totalTicks: 0,
consecutiveHits: 0,
// Loot System
lootInventory: {
@@ -415,8 +419,15 @@ export const useGameStore = create<GameStore>()(
meditateTicks = 0;
}
// Calculate effective regen with incursion and meditation
const effectiveRegen = baseRegen * (1 - incursionStrength) * meditationMultiplier;
// Calculate effective regen with dynamic special effects
// computeDynamicRegen handles: Mana Cascade, Mana Torrent, Desperate Wells, Steady Stream
let effectiveRegen = computeDynamicRegen(
effects,
baseRegen,
maxMana,
state.rawMana,
incursionStrength
) * meditationMultiplier;
// Mana regeneration
let rawMana = Math.min(state.rawMana + effectiveRegen * HOURS_PER_TICK, maxMana);
@@ -895,11 +906,28 @@ export const useGameStore = create<GameStore>()(
const overflowBonus = 1 + (state.skills.manaOverflow || 0) * 0.25;
cm = Math.floor(cm * overflowBonus);
// MANA_ECHO: 10% chance to gain double mana from clicks
let echoTriggered = false;
if (hasSpecial(effects, SPECIAL_EFFECTS.MANA_ECHO) && Math.random() < 0.1) {
cm *= 2;
echoTriggered = true;
}
const max = computeMaxMana(state, effects);
set({
rawMana: Math.min(state.rawMana + cm, max),
totalManaGathered: state.totalManaGathered + cm,
});
const newRawMana = Math.min(state.rawMana + cm, max);
if (echoTriggered) {
set({
rawMana: newRawMana,
totalManaGathered: state.totalManaGathered + cm,
log: [`✨ Mana Echo! Gained ${cm} mana (doubled)!`, ...state.log.slice(0, 49)],
});
} else {
set({
rawMana: newRawMana,
totalManaGathered: state.totalManaGathered + cm,
});
}
},
setAction: (action: GameAction) => {
@@ -1020,6 +1048,11 @@ export const useGameStore = create<GameStore>()(
const insightGained = state.loopInsight || calcInsight(state);
const total = state.insight + insightGained;
// Check for EMERGENCY_RESERVE before creating new state
const effects = getUnifiedEffects(state);
const maxMana = computeMaxMana(state, effects);
const hasEmergencyReserve = hasSpecial(effects, SPECIAL_EFFECTS.EMERGENCY_RESERVE);
// Keep some spells through temporal memory
let spellsToKeep: string[] = [];
if (state.skills.temporalMemory) {
@@ -1045,6 +1078,13 @@ export const useGameStore = create<GameStore>()(
});
}
// EMERGENCY_RESERVE: Keep 10% of max mana when starting new loop
if (hasEmergencyReserve) {
const reserveMana = Math.floor(maxMana * 0.1);
newState.rawMana = reserveMana;
newState.log = [`💫 Emergency Reserve preserved ${reserveMana} mana!`, ...newState.log.slice(0, 49)];
}
set(newState);
},