diff --git a/src/lib/game/data/enchantments/special-effects.ts b/src/lib/game/data/enchantments/special-effects.ts index a438a29..ff6a8d9 100644 --- a/src/lib/game/data/enchantments/special-effects.ts +++ b/src/lib/game/data/enchantments/special-effects.ts @@ -44,4 +44,34 @@ export const SPECIAL_EFFECTS: Record = { allowedEquipmentCategories: CASTER_AND_HANDS, effect: { type: 'special', specialId: 'overpower' } }, + first_strike: { + id: 'first_strike', + name: 'First Strike', + description: '+15% damage on first attack each floor', + category: 'special', + baseCapacityCost: 45, + maxStacks: 1, + allowedEquipmentCategories: CASTER_AND_HANDS, + effect: { type: 'special', specialId: 'firstStrike' } + }, + combo_master: { + id: 'combo_master', + name: 'Combo Master', + description: 'Every 5th attack deals 3x damage', + category: 'special', + baseCapacityCost: 65, + maxStacks: 1, + allowedEquipmentCategories: CASTER_AND_HANDS, + effect: { type: 'special', specialId: 'comboMaster' } + }, + adrenaline_rush: { + id: 'adrenaline_rush', + name: 'Adrenaline Rush', + description: 'Defeating enemy restores 5% mana', + category: 'special', + baseCapacityCost: 50, + maxStacks: 1, + allowedEquipmentCategories: CASTER_AND_HANDS, + effect: { type: 'special', specialId: 'adrenalineRush' } + }, }; diff --git a/src/lib/game/store.ts b/src/lib/game/store.ts index 253caa9..ce8872e 100755 --- a/src/lib/game/store.ts +++ b/src/lib/game/store.ts @@ -684,6 +684,10 @@ function makeInitial(overrides: Partial = {}): GameState { totalDamageDealt: 0, totalCraftsCompleted: 0, + // Combat special effect tracking + comboHitCount: 0, // Hit counter for COMBO_MASTER (every 5th attack) + floorHitCount: 0, // Hit counter for current floor (for FIRST_STRIKE) + // New equipment system equippedInstances: startingEquipment.equippedInstances, equipmentInstances: startingEquipment.equipmentInstances, @@ -1013,7 +1017,9 @@ export const useGameStore = create()( } // Combat - uses cast speed and spell casting - let { currentFloor, floorHP, floorMaxHP, maxFloorReached, signedPacts, castProgress, currentRoom } = state; + let { currentFloor, floorHP, floorMaxHP, maxFloorReached, signedPacts, castProgress, currentRoom, comboHitCount, floorHitCount } = state; + comboHitCount = comboHitCount || 0; + floorHitCount = floorHitCount || 0; const floorElement = getFloorElement(currentFloor); // Handle puzzle rooms separately @@ -1120,14 +1126,32 @@ export const useGameStore = create()( const effectiveArmor = Math.max(0, enemy.armor - armorPierce); dmg *= (1 - effectiveArmor); + // Increment hit counters + comboHitCount += 1; + floorHitCount += 1; + + // First Strike: +15% damage on first attack each floor + if (hasSpecial(effects, SPECIAL_EFFECTS.FIRST_STRIKE) && floorHitCount === 1) { + dmg *= 1.15; + log = [`⚡ First Strike! +15% damage!`, ...log.slice(0, 49)]; + } + + // Combo Master: Every 5th attack deals 3x damage + if (hasSpecial(effects, SPECIAL_EFFECTS.COMBO_MASTER) && comboHitCount % 5 === 0) { + dmg *= 3; + log = [`🌀 Combo Master! Triple damage!`, ...log.slice(0, 49)]; + } + // Executioner: +100% damage to enemies below 25% HP if (hasSpecial(effects, SPECIAL_EFFECTS.EXECUTIONER) && enemy.hp / enemy.maxHP < 0.25) { dmg *= 2; + log = [`💀 Executioner! Double damage!`, ...log.slice(0, 49)]; } // Berserker: +50% damage when below 50% mana if (hasSpecial(effects, SPECIAL_EFFECTS.BERSERKER) && rawMana < maxMana * 0.5) { dmg *= 1.5; + log = [`🔥 Berserker! +50% damage!`, ...log.slice(0, 49)]; } // Spell echo - chance to cast again @@ -1153,6 +1177,14 @@ export const useGameStore = create()( if (allDead) { // Floor cleared const wasGuardian = GUARDIANS[currentFloor]; + + // Adrenaline Rush: Defeating enemy restores 5% mana + if (hasSpecial(effects, SPECIAL_EFFECTS.ADRENALINE_RUSH)) { + const manaRestore = Math.floor(maxMana * 0.05); + rawMana = Math.min(rawMana + manaRestore, maxMana); + log = [`💚 Adrenaline Rush! Restored ${manaRestore} mana!`, ...log.slice(0, 49)]; + } + if (wasGuardian && !signedPacts.includes(currentFloor)) { signedPacts = [...signedPacts, currentFloor]; log = [`⚔️ ${wasGuardian.name} defeated! Pact signed! (${wasGuardian.pact}x)`, ...log.slice(0, 49)]; @@ -1175,8 +1207,9 @@ export const useGameStore = create()( floorHP = currentRoom.enemies[0]?.hp || floorMaxHP; maxFloorReached = Math.max(maxFloorReached, currentFloor); - // Reset cast progress on floor change + // Reset cast progress and floor hit counter on floor change castProgress = 0; + floorHitCount = 0; } } } else { @@ -1445,6 +1478,8 @@ export const useGameStore = create()( castProgress, golemancy, flowSurgeEndTime, + comboHitCount, + floorHitCount, ...craftingUpdates, }); }, @@ -2449,7 +2484,7 @@ export const useGameStore = create()( }), { name: 'mana-loop-storage', - version: 2, + version: 3, migrate: (persistedState: unknown, version: number) => { const state = persistedState as Record; // Migration from version 0/1 to version 2 - add missing fields @@ -2462,6 +2497,14 @@ export const useGameStore = create()( parallelStudyTarget: state.parallelStudyTarget ?? null, }; } + // Migration to version 3 - add combo hit counters + if (version < 3) { + return { + ...state, + comboHitCount: state.comboHitCount ?? 0, + floorHitCount: state.floorHitCount ?? 0, + }; + } return state; }, partialize: (state) => ({ @@ -2493,6 +2536,8 @@ export const useGameStore = create()( totalSpellsCast: state.totalSpellsCast, totalDamageDealt: state.totalDamageDealt, totalCraftsCompleted: state.totalCraftsCompleted, + comboHitCount: state.comboHitCount, + floorHitCount: state.floorHitCount, insight: state.insight, totalInsight: state.totalInsight, prestigeUpgrades: state.prestigeUpgrades, diff --git a/src/lib/game/store/combatSlice.ts b/src/lib/game/store/combatSlice.ts index 61c6254..5b1eeec 100755 --- a/src/lib/game/store/combatSlice.ts +++ b/src/lib/game/store/combatSlice.ts @@ -84,6 +84,8 @@ export const createCombatSlice = ( let pendingPactOffer = state.pendingPactOffer; const log = [...state.log]; const skills = state.skills; + let comboHitCount = state.comboHitCount || 0; + let floorHitCount = state.floorHitCount || 0; const floorElement = getFloorElement(currentFloor); @@ -98,15 +100,33 @@ export const createCombatSlice = ( let dmg = calcDamage(state, spellId, floorElement); dmg = dmg * effects.baseDamageMultiplier + effects.baseDamageBonus; + // Increment hit counters + comboHitCount += 1; + floorHitCount += 1; + + // First Strike: +15% damage on first attack each floor + if (hasSpecial(effects, SPECIAL_EFFECTS.FIRST_STRIKE) && floorHitCount === 1) { + dmg *= 1.15; + log.unshift('⚡ First Strike! +15% damage!'); + } + + // Combo Master: Every 5th attack deals 3x damage + if (hasSpecial(effects, SPECIAL_EFFECTS.COMBO_MASTER) && comboHitCount % 5 === 0) { + dmg *= 3; + log.unshift('🌀 Combo Master! Triple damage!'); + } + // Executioner: +100% damage to enemies below 25% HP if (hasSpecial(effects, SPECIAL_EFFECTS.EXECUTIONER) && floorHP / floorMaxHP < 0.25) { dmg *= 2; + log.unshift('💀 Executioner! Double damage!'); } // Berserker: +50% damage when below 50% mana const maxMana = 100; // Would need proper max mana calculation if (hasSpecial(effects, SPECIAL_EFFECTS.BERSERKER) && rawMana < maxMana * 0.5) { dmg *= 1.5; + log.unshift('🔥 Berserker! +50% damage!'); } // Spell echo - chance to cast again @@ -122,6 +142,14 @@ export const createCombatSlice = ( if (floorHP <= 0) { const wasGuardian = GUARDIANS[currentFloor]; + + // Adrenaline Rush: Defeating enemy restores 5% mana + if (hasSpecial(effects, SPECIAL_EFFECTS.ADRENALINE_RUSH)) { + const manaRestore = Math.floor(maxMana * 0.05); + rawMana = Math.min(rawMana + manaRestore, maxMana); + log.unshift(`💚 Adrenaline Rush! Restored ${manaRestore} mana!`); + } + if (wasGuardian && !signedPacts.includes(currentFloor)) { pendingPactOffer = currentFloor; log.unshift(`⚔️ ${wasGuardian.name} defeated! They offer a pact...`); @@ -137,6 +165,7 @@ export const createCombatSlice = ( floorHP = floorMaxHP; maxFloorReached = Math.max(maxFloorReached, currentFloor); castProgress = 0; + floorHitCount = 0; // Reset floor hit counter for new floor } } @@ -152,6 +181,8 @@ export const createCombatSlice = ( pendingPactOffer, castProgress, log, + comboHitCount, + floorHitCount, }; }, }); diff --git a/src/lib/game/types/game.ts b/src/lib/game/types/game.ts index be3b505..0fcda1a 100644 --- a/src/lib/game/types/game.ts +++ b/src/lib/game/types/game.ts @@ -185,6 +185,10 @@ export interface GameState { totalDamageDealt: number; totalCraftsCompleted: number; + // Combat special effect tracking + comboHitCount: number; // Hit counter for COMBO_MASTER (every 5th attack) + floorHitCount: number; // Hit counter for current floor (for FIRST_STRIKE) + // Prestige insight: number; totalInsight: number;