feat: Implement critical special effects (partial)
Some checks failed
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m9s
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:
@@ -22,7 +22,7 @@ import {
|
|||||||
BASE_UNLOCKED_EFFECTS,
|
BASE_UNLOCKED_EFFECTS,
|
||||||
ENCHANTING_UNLOCK_EFFECTS,
|
ENCHANTING_UNLOCK_EFFECTS,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { hasSpecial, SPECIAL_EFFECTS } from './upgrade-effects';
|
import { hasSpecial, SPECIAL_EFFECTS, computeDynamicRegen } from './upgrade-effects';
|
||||||
import { getUnifiedEffects } from './effects';
|
import { getUnifiedEffects } from './effects';
|
||||||
import { SKILL_EVOLUTION_PATHS } from './skill-evolution';
|
import { SKILL_EVOLUTION_PATHS } from './skill-evolution';
|
||||||
import {
|
import {
|
||||||
@@ -206,6 +206,9 @@ function makeInitial(overrides: Partial<GameState> = {}): GameState {
|
|||||||
skillUpgrades: overrides.skillUpgrades || {},
|
skillUpgrades: overrides.skillUpgrades || {},
|
||||||
skillTiers: overrides.skillTiers || {},
|
skillTiers: overrides.skillTiers || {},
|
||||||
parallelStudyTarget: null,
|
parallelStudyTarget: null,
|
||||||
|
studyStartedAt: null,
|
||||||
|
consecutiveStudyHours: 0,
|
||||||
|
lastStudyCost: 0,
|
||||||
|
|
||||||
// New equipment system
|
// New equipment system
|
||||||
equippedInstances: startingEquipment.equippedInstances,
|
equippedInstances: startingEquipment.equippedInstances,
|
||||||
@@ -256,6 +259,7 @@ function makeInitial(overrides: Partial<GameState> = {}): GameState {
|
|||||||
elementChain: [],
|
elementChain: [],
|
||||||
},
|
},
|
||||||
totalTicks: 0,
|
totalTicks: 0,
|
||||||
|
consecutiveHits: 0,
|
||||||
|
|
||||||
// Loot System
|
// Loot System
|
||||||
lootInventory: {
|
lootInventory: {
|
||||||
@@ -415,8 +419,15 @@ export const useGameStore = create<GameStore>()(
|
|||||||
meditateTicks = 0;
|
meditateTicks = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate effective regen with incursion and meditation
|
// Calculate effective regen with dynamic special effects
|
||||||
const effectiveRegen = baseRegen * (1 - incursionStrength) * meditationMultiplier;
|
// computeDynamicRegen handles: Mana Cascade, Mana Torrent, Desperate Wells, Steady Stream
|
||||||
|
let effectiveRegen = computeDynamicRegen(
|
||||||
|
effects,
|
||||||
|
baseRegen,
|
||||||
|
maxMana,
|
||||||
|
state.rawMana,
|
||||||
|
incursionStrength
|
||||||
|
) * meditationMultiplier;
|
||||||
|
|
||||||
// Mana regeneration
|
// Mana regeneration
|
||||||
let rawMana = Math.min(state.rawMana + effectiveRegen * HOURS_PER_TICK, maxMana);
|
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;
|
const overflowBonus = 1 + (state.skills.manaOverflow || 0) * 0.25;
|
||||||
cm = Math.floor(cm * overflowBonus);
|
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);
|
const max = computeMaxMana(state, effects);
|
||||||
|
const newRawMana = Math.min(state.rawMana + cm, max);
|
||||||
|
|
||||||
|
if (echoTriggered) {
|
||||||
set({
|
set({
|
||||||
rawMana: Math.min(state.rawMana + cm, max),
|
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,
|
totalManaGathered: state.totalManaGathered + cm,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setAction: (action: GameAction) => {
|
setAction: (action: GameAction) => {
|
||||||
@@ -1020,6 +1048,11 @@ export const useGameStore = create<GameStore>()(
|
|||||||
const insightGained = state.loopInsight || calcInsight(state);
|
const insightGained = state.loopInsight || calcInsight(state);
|
||||||
const total = state.insight + insightGained;
|
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
|
// Keep some spells through temporal memory
|
||||||
let spellsToKeep: string[] = [];
|
let spellsToKeep: string[] = [];
|
||||||
if (state.skills.temporalMemory) {
|
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);
|
set(newState);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -472,6 +472,11 @@ export interface GameState {
|
|||||||
// Parallel Study Target (for Parallel Mind milestone upgrade)
|
// Parallel Study Target (for Parallel Mind milestone upgrade)
|
||||||
parallelStudyTarget: StudyTarget | null;
|
parallelStudyTarget: StudyTarget | null;
|
||||||
|
|
||||||
|
// Study tracking for special effects
|
||||||
|
studyStartedAt: number | null; // Tick when study started (for STUDY_RUSH)
|
||||||
|
consecutiveStudyHours: number; // Consecutive hours studying (for STUDY_MOMENTUM)
|
||||||
|
lastStudyCost: number; // Cost of starting current study (for STUDY_REFUND)
|
||||||
|
|
||||||
// Prestige
|
// Prestige
|
||||||
insight: number;
|
insight: number;
|
||||||
totalInsight: number;
|
totalInsight: number;
|
||||||
@@ -486,6 +491,7 @@ export interface GameState {
|
|||||||
// Combo System
|
// Combo System
|
||||||
combo: ComboState;
|
combo: ComboState;
|
||||||
totalTicks: number; // Total ticks this loop (for combo timing)
|
totalTicks: number; // Total ticks this loop (for combo timing)
|
||||||
|
consecutiveHits: number; // Consecutive hits for BATTLE_FURY tracking
|
||||||
|
|
||||||
// Loot System
|
// Loot System
|
||||||
lootInventory: LootInventory;
|
lootInventory: LootInventory;
|
||||||
|
|||||||
Reference in New Issue
Block a user