pack
This commit is contained in:
@@ -98,6 +98,43 @@ export function getFloorElement(floor: number): string {
|
||||
return FLOOR_ELEM_CYCLE[(floor - 1) % 8];
|
||||
}
|
||||
|
||||
// Get all spells from equipped caster weapons (staves, wands, etc.)
|
||||
// Returns array of { spellId, equipmentInstanceId }
|
||||
function getActiveEquipmentSpells(
|
||||
equippedInstances: Record<string, string | null>,
|
||||
equipmentInstances: Record<string, EquipmentInstance>
|
||||
): Array<{ spellId: string; equipmentId: string }> {
|
||||
const spells: Array<{ spellId: string; equipmentId: string }> = [];
|
||||
|
||||
// Check main hand and off hand for caster equipment
|
||||
const weaponSlots = ['mainHand', 'offHand'] as const;
|
||||
|
||||
for (const slot of weaponSlots) {
|
||||
const instanceId = equippedInstances[slot];
|
||||
if (!instanceId) continue;
|
||||
|
||||
const instance = equipmentInstances[instanceId];
|
||||
if (!instance) continue;
|
||||
|
||||
// Check if this is a caster-type equipment
|
||||
const equipType = EQUIPMENT_TYPES[instance.typeId];
|
||||
if (!equipType || equipType.category !== 'caster') continue;
|
||||
|
||||
// Get spells from enchantments
|
||||
for (const ench of instance.enchantments) {
|
||||
const effectDef = ENCHANTMENT_EFFECTS[ench.effectId];
|
||||
if (effectDef?.effect.type === 'spell' && effectDef.effect.spellId) {
|
||||
spells.push({
|
||||
spellId: effectDef.effect.spellId,
|
||||
equipmentId: instanceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spells;
|
||||
}
|
||||
|
||||
// ─── Computed Stats Functions ─────────────────────────────────────────────────
|
||||
|
||||
// Helper to get effective skill level accounting for tiers
|
||||
@@ -186,8 +223,8 @@ export function computeRegen(
|
||||
* Compute regen with dynamic special effects (needs current mana, max mana, incursion)
|
||||
*/
|
||||
export function computeEffectiveRegen(
|
||||
state: Pick<GameState, 'skills' | 'prestigeUpgrades' | 'rawMana' | 'incursionStrength' | 'skillUpgrades' | 'skillTiers'>,
|
||||
effects?: ComputedEffects
|
||||
state: Pick<GameState, 'skills' | 'prestigeUpgrades' | 'rawMana' | 'incursionStrength' | 'skillUpgrades' | 'skillTiers' | 'equipmentInstances' | 'equippedInstances'>,
|
||||
effects?: ComputedEffects | UnifiedEffects
|
||||
): number {
|
||||
// Base regen from existing function
|
||||
let regen = computeRegen(state, effects);
|
||||
@@ -369,7 +406,12 @@ function deductSpellCost(
|
||||
function makeInitial(overrides: Partial<GameState> = {}): GameState {
|
||||
const pu = overrides.prestigeUpgrades || {};
|
||||
const startFloor = 1 + (pu.spireKey || 0) * 2;
|
||||
const elemMax = computeElementMax({ skills: overrides.skills || {}, prestigeUpgrades: pu });
|
||||
const elemMax = computeElementMax({
|
||||
skills: overrides.skills || {},
|
||||
prestigeUpgrades: pu,
|
||||
skillUpgrades: overrides.skillUpgrades || {},
|
||||
skillTiers: overrides.skillTiers || {}
|
||||
});
|
||||
|
||||
const elements: Record<string, { current: number; max: number; unlocked: boolean }> = {};
|
||||
Object.keys(ELEMENTS).forEach((k) => {
|
||||
@@ -695,18 +737,47 @@ export const useGameStore = create<GameStore>()(
|
||||
}
|
||||
}
|
||||
|
||||
// Combat - uses cast speed and spell casting
|
||||
let { currentFloor, floorHP, floorMaxHP, maxFloorReached, signedPacts, castProgress } = state;
|
||||
// Combat - MULTI-SPELL casting from all equipped weapons
|
||||
let { currentFloor, floorHP, floorMaxHP, maxFloorReached, signedPacts, equipmentSpellStates } = state;
|
||||
const floorElement = getFloorElement(currentFloor);
|
||||
|
||||
if (state.currentAction === 'climb') {
|
||||
const spellId = state.activeSpell;
|
||||
const spellDef = SPELLS_DEF[spellId];
|
||||
// Get all spells from equipped caster weapons
|
||||
const activeSpells = getActiveEquipmentSpells(state.equippedInstances, state.equipmentInstances);
|
||||
|
||||
if (spellDef) {
|
||||
// Compute attack speed from quickCast skill and upgrades
|
||||
const baseAttackSpeed = 1 + (state.skills.quickCast || 0) * 0.05;
|
||||
const totalAttackSpeed = baseAttackSpeed * effects.attackSpeedMultiplier;
|
||||
// Initialize spell states if needed
|
||||
if (!equipmentSpellStates) {
|
||||
equipmentSpellStates = [];
|
||||
}
|
||||
|
||||
// Ensure we have state for all active spells
|
||||
for (const { spellId, equipmentId } of activeSpells) {
|
||||
if (!equipmentSpellStates.find(s => s.spellId === spellId && s.sourceEquipment === equipmentId)) {
|
||||
equipmentSpellStates.push({
|
||||
spellId,
|
||||
sourceEquipment: equipmentId,
|
||||
castProgress: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Remove states for spells that are no longer equipped
|
||||
equipmentSpellStates = equipmentSpellStates.filter(es =>
|
||||
activeSpells.some(as => as.spellId === es.spellId && as.equipmentId === es.sourceEquipment)
|
||||
);
|
||||
|
||||
// Compute attack speed from quickCast skill and upgrades
|
||||
const baseAttackSpeed = 1 + (state.skills.quickCast || 0) * 0.05;
|
||||
const totalAttackSpeed = baseAttackSpeed * effects.attackSpeedMultiplier;
|
||||
|
||||
// Process each active spell
|
||||
for (const { spellId, equipmentId } of activeSpells) {
|
||||
const spellDef = SPELLS_DEF[spellId];
|
||||
if (!spellDef) continue;
|
||||
|
||||
// Get or create spell state
|
||||
let spellState = equipmentSpellStates.find(s => s.spellId === spellId && s.sourceEquipment === equipmentId);
|
||||
if (!spellState) continue;
|
||||
|
||||
// Get spell cast speed (casts per hour, default 1)
|
||||
const spellCastSpeed = spellDef.castSpeed || 1;
|
||||
@@ -715,10 +786,10 @@ export const useGameStore = create<GameStore>()(
|
||||
const progressPerTick = HOURS_PER_TICK * spellCastSpeed * totalAttackSpeed;
|
||||
|
||||
// Accumulate cast progress
|
||||
castProgress = (castProgress || 0) + progressPerTick;
|
||||
spellState = { ...spellState, castProgress: spellState.castProgress + progressPerTick };
|
||||
|
||||
// Process complete casts
|
||||
while (castProgress >= 1 && canAffordSpellCost(spellDef.cost, rawMana, elements)) {
|
||||
while (spellState.castProgress >= 1 && canAffordSpellCost(spellDef.cost, rawMana, elements)) {
|
||||
// Deduct cost
|
||||
const afterCost = deductSpellCost(spellDef.cost, rawMana, elements);
|
||||
rawMana = afterCost.rawMana;
|
||||
@@ -731,9 +802,9 @@ export const useGameStore = create<GameStore>()(
|
||||
// Apply upgrade damage multipliers and bonuses
|
||||
dmg = dmg * effects.baseDamageMultiplier + effects.baseDamageBonus;
|
||||
|
||||
// Executioner: +100% damage to enemies below 25% HP
|
||||
if (hasSpecial(effects, SPECIAL_EFFECTS.EXECUTIONER) && floorHP / floorMaxHP < 0.25) {
|
||||
dmg *= 2;
|
||||
// Overpower: +50% damage when mana above 80%
|
||||
if (hasSpecial(effects, SPECIAL_EFFECTS.OVERPOWER) && rawMana >= maxMana * 0.8) {
|
||||
dmg *= 1.5;
|
||||
}
|
||||
|
||||
// Berserker: +50% damage when below 50% mana
|
||||
@@ -745,7 +816,7 @@ export const useGameStore = create<GameStore>()(
|
||||
const echoChance = (skills.spellEcho || 0) * 0.1;
|
||||
if (Math.random() < echoChance) {
|
||||
dmg *= 2;
|
||||
log = [`✨ Spell Echo! Double damage!`, ...log.slice(0, 49)];
|
||||
log = [`✨ Spell Echo! ${spellDef.name} deals double damage!`, ...log.slice(0, 49)];
|
||||
}
|
||||
|
||||
// Lifesteal effect
|
||||
@@ -759,7 +830,7 @@ export const useGameStore = create<GameStore>()(
|
||||
floorHP = Math.max(0, floorHP - dmg);
|
||||
|
||||
// Reduce cast progress by 1 (one cast completed)
|
||||
castProgress -= 1;
|
||||
spellState = { ...spellState, castProgress: spellState.castProgress - 1 };
|
||||
|
||||
if (floorHP <= 0) {
|
||||
// Floor cleared
|
||||
@@ -781,13 +852,17 @@ export const useGameStore = create<GameStore>()(
|
||||
floorHP = floorMaxHP;
|
||||
maxFloorReached = Math.max(maxFloorReached, currentFloor);
|
||||
|
||||
// Reset cast progress on floor change
|
||||
castProgress = 0;
|
||||
// Reset ALL spell progress on floor change
|
||||
equipmentSpellStates = equipmentSpellStates.map(s => ({ ...s, castProgress: 0 }));
|
||||
spellState = { ...spellState, castProgress: 0 };
|
||||
break; // Exit the while loop - new floor
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not enough mana - pause casting (keep progress)
|
||||
castProgress = castProgress || 0;
|
||||
|
||||
// Update the spell state in the array
|
||||
equipmentSpellStates = equipmentSpellStates.map(s =>
|
||||
(s.spellId === spellId && s.sourceEquipment === equipmentId) ? spellState : s
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,7 +877,7 @@ export const useGameStore = create<GameStore>()(
|
||||
floorMaxHP,
|
||||
maxFloorReached,
|
||||
signedPacts,
|
||||
castProgress,
|
||||
equipmentSpellStates,
|
||||
incursionStrength,
|
||||
currentStudyTarget,
|
||||
skills,
|
||||
@@ -837,8 +912,8 @@ export const useGameStore = create<GameStore>()(
|
||||
spells,
|
||||
elements,
|
||||
log,
|
||||
castProgress,
|
||||
});
|
||||
equipmentSpellStates,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -861,7 +936,7 @@ export const useGameStore = create<GameStore>()(
|
||||
elements,
|
||||
unlockedEffects,
|
||||
log,
|
||||
castProgress,
|
||||
equipmentSpellStates,
|
||||
...craftingUpdates,
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user