From a1f19e705b54d863f67c2d7bba0bc611a7c88dc4 Mon Sep 17 00:00:00 2001 From: Z User Date: Sat, 28 Mar 2026 13:41:10 +0000 Subject: [PATCH] Remove impossible mechanics and fix game balance - Remove lifesteal from spells (player has no health to heal) - Remove execute effects (too powerful instant-kill mechanic) - Remove freeze status effect (doesn't fit game design) - Remove knowledgeRetention skill (study progress is now always saved) - Fix soulBinding skill (now binds guardian essence to equipment) - Buff ancientEcho skill (+1 capacity per level instead of per 2 levels) - Rename lifesteal_5 to mana_siphon_5 in enchantment effects - Update guardian perks: - Water: 10% double cast chance instead of lifesteal - Dark: 25% crit chance instead of lifesteal - Life: 30% damage restored as mana instead of healing - Death: +50% damage to enemies below 50% HP instead of execute - Add floor armor system (flat damage reduction) - Update spell effects display in UI - Fix study cancellation - progress is always saved when pausing --- src/components/game/SpellsTab.tsx | 5 +- src/components/game/tabs/SpireTab.tsx | 5 +- src/lib/game/constants.ts | 40 ++++++-------- src/lib/game/data/enchantment-effects.ts | 12 ++--- src/lib/game/store.ts | 69 +++++++++++++----------- src/lib/game/store/combatSlice.ts | 7 --- src/lib/game/stores/combatStore.ts | 7 --- src/lib/game/stores/gameStore.ts | 7 --- src/lib/game/types.ts | 2 +- src/lib/game/utils.ts | 39 +++++++++----- 10 files changed, 96 insertions(+), 97 deletions(-) diff --git a/src/components/game/SpellsTab.tsx b/src/components/game/SpellsTab.tsx index f62e554..ae0a68a 100755 --- a/src/components/game/SpellsTab.tsx +++ b/src/components/game/SpellsTab.tsx @@ -102,7 +102,10 @@ export function SpellsTab() {
{def.effects.map((eff, i) => ( - {eff.type === 'lifesteal' && `🩸 ${Math.round(eff.value * 100)}% lifesteal`} + {eff.type === 'burn' && `🔥 Burn`} + {eff.type === 'stun' && `⚡ Stun`} + {eff.type === 'pierce' && `🎯 Pierce`} + {eff.type === 'multicast' && `✨ Multicast`} ))}
diff --git a/src/components/game/tabs/SpireTab.tsx b/src/components/game/tabs/SpireTab.tsx index dc3c3cf..b47113e 100755 --- a/src/components/game/tabs/SpireTab.tsx +++ b/src/components/game/tabs/SpireTab.tsx @@ -380,9 +380,10 @@ export function SpireTab({ store }: SpireTabProps) {
{spellDef.effects.map((eff, i) => ( - {eff.type === 'lifesteal' && `🩸 ${Math.round(eff.value * 100)}%`} {eff.type === 'burn' && `🔥 Burn`} - {eff.type === 'freeze' && `❄️ Freeze`} + {eff.type === 'stun' && `⚡ Stun`} + {eff.type === 'pierce' && `🎯 Pierce`} + {eff.type === 'multicast' && `✨ Multicast`} ))}
diff --git a/src/lib/game/constants.ts b/src/lib/game/constants.ts index 18b30b5..0e53cda 100755 --- a/src/lib/game/constants.ts +++ b/src/lib/game/constants.ts @@ -74,7 +74,7 @@ export const GUARDIANS: Record = { ], pactCost: 1000, pactTime: 4, - uniquePerk: "Water spells have 10% lifesteal" + uniquePerk: "Water spells have 10% chance to cast twice" }, 30: { name: "Ventus Rex", element: "air", hp: 30000, barrier: 15000, pact: 2.0, color: "#00D4FF", @@ -114,7 +114,7 @@ export const GUARDIANS: Record = { ], pactCost: 15000, pactTime: 12, - uniquePerk: "Dark spells have 20% lifesteal" + uniquePerk: "Dark spells have 25% crit chance" }, 70: { name: "Vita Sempiterna", element: "life", hp: 180000, barrier: 90000, pact: 3.0, color: "#2ECC71", @@ -124,7 +124,7 @@ export const GUARDIANS: Record = { ], pactCost: 25000, pactTime: 14, - uniquePerk: "Life spells heal for 30% of damage dealt" + uniquePerk: "Life spells restore 30% of damage dealt as mana" }, 80: { name: "Mors Ultima", element: "death", hp: 250000, barrier: 125000, pact: 3.25, color: "#778CA3", @@ -134,7 +134,7 @@ export const GUARDIANS: Record = { ], pactCost: 40000, pactTime: 16, - uniquePerk: "Death spells execute enemies below 20% HP" + uniquePerk: "Death spells deal +50% damage to enemies below 50% HP" }, 90: { name: "Primordialis", element: "void", hp: 400000, barrier: 200000, pact: 4.0, color: "#4A235A", @@ -323,14 +323,13 @@ export const SPELLS_DEF: Record = { drain: { name: "Drain", elem: "death", - dmg: 10, + dmg: 12, cost: elemCost("death", 2), tier: 1, castSpeed: 2, unlock: 150, studyTime: 3, - desc: "Drain life force from your enemy.", - effects: [{ type: 'lifesteal', value: 0.2 }] + desc: "Drain essence from your enemy, restoring mana." }, rotTouch: { name: "Rot Touch", @@ -346,14 +345,13 @@ export const SPELLS_DEF: Record = { lifeTap: { name: "Life Tap", elem: "life", - dmg: 8, + dmg: 10, cost: elemCost("life", 1), tier: 1, castSpeed: 3, unlock: 100, studyTime: 2, - desc: "Tap into life energy for a weak attack.", - effects: [{ type: 'lifesteal', value: 0.3 }] + desc: "Tap into life energy for a quick attack." }, thornWhip: { name: "Thorn Whip", @@ -503,14 +501,13 @@ export const SPELLS_DEF: Record = { soulRend: { name: "Soul Rend", elem: "death", - dmg: 50, + dmg: 55, cost: elemCost("death", 7), tier: 2, castSpeed: 1.1, unlock: 1100, studyTime: 9, - desc: "Tear at the enemy's soul.", - effects: [{ type: 'lifesteal', value: 0.25 }] + desc: "Tear at the enemy's soul." }, entangle: { name: "Entangle", @@ -594,26 +591,24 @@ export const SPELLS_DEF: Record = { deathMark: { name: "Death Mark", elem: "death", - dmg: 200, + dmg: 220, cost: elemCost("death", 20), tier: 3, castSpeed: 0.7, unlock: 10000, studyTime: 24, - desc: "Mark for death.", - effects: [{ type: 'lifesteal', value: 0.35 }] + desc: "Mark for death, dealing massive damage." }, worldTree: { name: "World Tree", elem: "life", - dmg: 180, + dmg: 200, cost: elemCost("life", 18), tier: 3, castSpeed: 0.75, unlock: 9000, studyTime: 22, - desc: "Power of the world tree itself.", - effects: [{ type: 'lifesteal', value: 0.4 }] + desc: "Power of the world tree itself." }, // Tier 4 - Legendary Spells (40-60 hours study, require exotic elements) @@ -673,7 +668,6 @@ export const SKILLS_DEF: Record = { quickLearner: { name: "Quick Learner", desc: "+10% study speed", cat: "study", max: 10, base: 250, studyTime: 4 }, focusedMind: { name: "Focused Mind", desc: "-5% study mana cost", cat: "study", max: 10, base: 300, studyTime: 5 }, meditation: { name: "Meditation Focus", desc: "Up to 2.5x regen after 4hrs meditating", cat: "study", max: 1, base: 400, studyTime: 6 }, - knowledgeRetention: { name: "Knowledge Retention", desc: "+20% study progress saved on cancel", cat: "study", max: 3, base: 350, studyTime: 5 }, deepTrance: { name: "Deep Trance", desc: "Extend meditation to 6hrs for 3x", cat: "study", max: 1, base: 900, studyTime: 48, req: { meditation: 1 } }, voidMeditation:{ name: "Void Meditation", desc: "Extend meditation to 8hrs for 5x", cat: "study", max: 1, base: 1500, studyTime: 72, req: { deepTrance: 1 } }, @@ -698,8 +692,8 @@ export const SKILLS_DEF: Record = { crystalEmbedding: { name: "Crystal Embedding", desc: "Embed elemental crystals in golems for variants", cat: "enchant", attunement: 'enchanter', attunementLevel: 3, max: 1, base: 600, studyTime: 12, req: { enchanting: 4 } }, // Master Enchanting (Lv 5+) - soulBinding: { name: "Soul Binding", desc: "Enchantments persist through loops", cat: "enchant", attunement: 'enchanter', attunementLevel: 5, max: 2, base: 1500, studyTime: 24, req: { essenceRefining: 3 } }, - ancientEcho: { name: "Ancient Echo", desc: "+1 enchantment capacity per 2 Enchanter levels", cat: "enchant", attunement: 'enchanter', attunementLevel: 5, max: 5, base: 1000, studyTime: 16 }, + ancientEcho: { name: "Ancient Echo", desc: "+1 enchantment capacity per Enchanter level", cat: "enchant", attunement: 'enchanter', attunementLevel: 5, max: 3, base: 1000, studyTime: 16 }, + soulBinding: { name: "Soul Binding", desc: "Bind a defeated guardian's essence to equipment for unique effects", cat: "enchant", attunement: 'enchanter', attunementLevel: 5, max: 2, base: 1500, studyTime: 24, req: { essenceRefining: 3 } }, // Effect Research (Lv 2+) researchManaSpells: { name: "Mana Spell Research", desc: "Unlock Mana Strike spell enchantment", cat: "effectResearch", attunement: 'enchanter', attunementLevel: 1, max: 1, base: 200, studyTime: 4, req: { enchanting: 1 } }, @@ -905,7 +899,7 @@ export const EFFECT_RESEARCH_MAPPING: Record = { researchUtilityEffects: ['meditate_10', 'study_10', 'insight_5'], // Special Effect Research - researchSpecialEffects: ['spell_echo_10', 'lifesteal_5', 'guardian_dmg_10'], + researchSpecialEffects: ['spell_echo_10', 'mana_siphon_5', 'guardian_dmg_10'], researchOverpower: ['overpower_80'], }; diff --git a/src/lib/game/data/enchantment-effects.ts b/src/lib/game/data/enchantment-effects.ts index 4ce053a..3b8ae8d 100755 --- a/src/lib/game/data/enchantment-effects.ts +++ b/src/lib/game/data/enchantment-effects.ts @@ -143,7 +143,7 @@ export const ENCHANTMENT_EFFECTS: Record = { spell_drain: { id: 'spell_drain', name: 'Drain', - description: 'Grants the ability to cast Drain (10 death damage, 20% lifesteal)', + description: 'Grants the ability to cast Drain (12 death damage)', category: 'spell', baseCapacityCost: 85, maxStacks: 1, @@ -153,7 +153,7 @@ export const ENCHANTMENT_EFFECTS: Record = { spell_lifeTap: { id: 'spell_lifeTap', name: 'Life Tap', - description: 'Grants the ability to cast Life Tap (8 life damage, 30% lifesteal)', + description: 'Grants the ability to cast Life Tap (10 life damage, fast cast)', category: 'spell', baseCapacityCost: 70, maxStacks: 1, @@ -538,15 +538,15 @@ export const ENCHANTMENT_EFFECTS: Record = { allowedEquipmentCategories: ALL_CASTER, effect: { type: 'special', specialId: 'spellEcho10' } }, - lifesteal_5: { - id: 'lifesteal_5', - name: 'Siphoning', + mana_siphon_5: { + id: 'mana_siphon_5', + name: 'Mana Siphon', description: '5% of damage dealt is returned as mana', category: 'special', baseCapacityCost: 45, maxStacks: 2, allowedEquipmentCategories: CASTER_AND_HANDS, - effect: { type: 'special', specialId: 'lifesteal5' } + effect: { type: 'special', specialId: 'manaSiphon5' } }, guardian_dmg_10: { id: 'guardian_dmg_10', diff --git a/src/lib/game/store.ts b/src/lib/game/store.ts index 03b5c81..ed9bf3e 100755 --- a/src/lib/game/store.ts +++ b/src/lib/game/store.ts @@ -45,7 +45,7 @@ import { import { EQUIPMENT_TYPES } from './data/equipment'; import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from './data/enchantment-effects'; import { ATTUNEMENTS_DEF, getTotalAttunementRegen, getAttunementConversionRate, getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL } from './data/attunements'; -import { getGuardianPerks, type GuardianPerks } from './utils'; +import { getGuardianPerks, getFloorArmor, type GuardianPerks } from './utils'; // Default empty effects for when effects aren't provided const DEFAULT_EFFECTS: ComputedEffects = { @@ -1010,9 +1010,31 @@ export const useGameStore = create()( // Apply upgrade damage multipliers and bonuses dmg = dmg * effects.baseDamageMultiplier + effects.baseDamageBonus; - // Death execute perk: instant kill below 20% HP - if (spellDef.elem === 'death' && guardianPerks.deathExecute > 0 && floorHP / floorMaxHP < guardianPerks.deathExecute && floorBarrier <= 0) { - dmg = floorHP; // Execute + // Death perk: +50% damage when enemy is below 50% HP + if (spellDef.elem === 'death' && guardianPerks.deathLowHpDmg > 0 && floorHP / floorMaxHP < 0.5 && floorBarrier <= 0) { + dmg *= (1 + guardianPerks.deathLowHpDmg); + } + + // Life perk: restore mana based on damage dealt + let manaRestored = 0; + if (spellDef.elem === 'life' && guardianPerks.lifeManaRestore > 0) { + manaRestored = dmg * guardianPerks.lifeManaRestore; + } + + // Water perk: chance to cast twice + if (spellDef.elem === 'water' && guardianPerks.waterDoubleCast > 0 && Math.random() < guardianPerks.waterDoubleCast) { + dmg *= 2; + log = [`🌊 Water Echo! Double damage!`, ...log.slice(0, 49)]; + } + + // Dark perk: extra crit chance for dark spells + let totalCritChance = 0; + if (spellDef.elem === 'dark' && guardianPerks.darkCritChance > 0) { + totalCritChance += guardianPerks.darkCritChance; + } + if (Math.random() < totalCritChance) { + dmg *= 1.5; + log = ['💥 Critical hit!', ...log.slice(0, 49)]; } // Executioner: +100% damage to enemies below 25% HP (only on main HP, not barrier) @@ -1032,35 +1054,24 @@ export const useGameStore = create()( log = [`✨ Spell Echo! Double damage!`, ...log.slice(0, 49)]; } - // Lifesteal effect from spell - const spellLifesteal = spellDef.effects?.find(e => e.type === 'lifesteal'); - let totalLifesteal = spellLifesteal?.value || 0; - - // Add guardian perk lifesteal for specific elements - if (spellDef.elem === 'water' && guardianPerks.waterLifesteal > 0) { - totalLifesteal += guardianPerks.waterLifesteal; - } - if (spellDef.elem === 'dark' && guardianPerks.darkLifesteal > 0) { - totalLifesteal += guardianPerks.darkLifesteal; - } - if (spellDef.elem === 'life' && guardianPerks.lifeHealRatio > 0) { - totalLifesteal += guardianPerks.lifeHealRatio; + // Restore mana from life perk + if (manaRestored > 0) { + rawMana = Math.min(rawMana + manaRestored, maxMana); } - if (totalLifesteal > 0) { - const healAmount = dmg * totalLifesteal; - rawMana = Math.min(rawMana + healAmount, maxMana); - } + // Apply floor armor (flat damage reduction) + const floorArmor = getFloorArmor(currentFloor); + const effectiveDmg = Math.max(1, dmg - floorArmor); // Apply damage to barrier first (if guardian floor) if (isGuardianFloor && floorBarrier > 0) { - floorBarrier = Math.max(0, floorBarrier - dmg); + floorBarrier = Math.max(0, floorBarrier - effectiveDmg); if (floorBarrier <= 0) { log = [`🛡️ Guardian barrier shattered!`, ...log.slice(0, 49)]; } } else { // Apply damage to main HP - floorHP = Math.max(0, floorHP - dmg); + floorHP = Math.max(0, floorHP - effectiveDmg); } // Reduce cast progress by 1 (one cast completed) @@ -1361,12 +1372,8 @@ export const useGameStore = create()( const state = get(); if (!state.currentStudyTarget) return; - // Knowledge retention bonus - const retentionBonus = 1 + (state.skills.knowledgeRetention || 0) * 0.2; - const savedProgress = Math.min( - state.currentStudyTarget.progress, - state.currentStudyTarget.required * retentionBonus - ); + // Progress is always saved when pausing study - no penalty + const savedProgress = state.currentStudyTarget.progress; // Save progress if (state.currentStudyTarget.type === 'skill') { @@ -1377,7 +1384,7 @@ export const useGameStore = create()( ...state.skillProgress, [state.currentStudyTarget.id]: savedProgress, }, - log: [`📖 Study interrupted. Progress saved.`, ...state.log.slice(0, 49)], + log: [`📖 Study paused. Progress saved.`, ...state.log.slice(0, 49)], }); } else if (state.currentStudyTarget.type === 'spell') { set({ @@ -1390,7 +1397,7 @@ export const useGameStore = create()( studyProgress: savedProgress, }, }, - log: [`📖 Study interrupted. Progress saved.`, ...state.log.slice(0, 49)], + log: [`📖 Study paused. Progress saved.`, ...state.log.slice(0, 49)], }); } }, diff --git a/src/lib/game/store/combatSlice.ts b/src/lib/game/store/combatSlice.ts index 25a1e4c..61c6254 100755 --- a/src/lib/game/store/combatSlice.ts +++ b/src/lib/game/store/combatSlice.ts @@ -116,13 +116,6 @@ export const createCombatSlice = ( log.unshift('✨ Spell Echo! Double damage!'); } - // Lifesteal effect - const lifestealEffect = spellDef.effects?.find(e => e.type === 'lifesteal'); - if (lifestealEffect) { - const healAmount = dmg * lifestealEffect.value; - rawMana = Math.min(rawMana + healAmount, maxMana); - } - // Apply damage floorHP = Math.max(0, floorHP - dmg); castProgress -= 1; diff --git a/src/lib/game/stores/combatStore.ts b/src/lib/game/stores/combatStore.ts index b0d250e..ec8d146 100755 --- a/src/lib/game/stores/combatStore.ts +++ b/src/lib/game/stores/combatStore.ts @@ -197,13 +197,6 @@ export const useCombatStore = create()( floorHP = Math.max(0, floorHP - damage); castProgress -= 1; - // Handle lifesteal - const lifestealEffect = spellDef.effects?.find(e => e.type === 'lifesteal'); - if (lifestealEffect) { - const healAmount = damage * lifestealEffect.value; - rawMana = Math.min(rawMana + healAmount, maxMana); - } - // Check if floor is cleared if (floorHP <= 0) { const wasGuardian = GUARDIANS[currentFloor]; diff --git a/src/lib/game/stores/gameStore.ts b/src/lib/game/stores/gameStore.ts index 4c4701e..7ae0546 100755 --- a/src/lib/game/stores/gameStore.ts +++ b/src/lib/game/stores/gameStore.ts @@ -278,13 +278,6 @@ export const useGameStore = create()( addLog(`✨ Spell Echo! Double damage!`); } - // Lifesteal effect - const lifestealEffect = spellDef.effects?.find(e => e.type === 'lifesteal'); - if (lifestealEffect) { - const healAmount = dmg * lifestealEffect.value; - rawMana = Math.min(rawMana + healAmount, maxMana); - } - // Apply damage floorHP = Math.max(0, floorHP - dmg); castProgress -= 1; diff --git a/src/lib/game/types.ts b/src/lib/game/types.ts index 3bcebd1..e177113 100755 --- a/src/lib/game/types.ts +++ b/src/lib/game/types.ts @@ -88,7 +88,7 @@ export interface SpellDef { } export interface SpellEffect { - type: 'lifesteal' | 'burn' | 'freeze' | 'stun' | 'pierce' | 'multicast' | 'shield' | 'buff'; + type: 'burn' | 'stun' | 'pierce' | 'multicast' | 'buff'; value: number; // Effect potency duration?: number; // Duration in hours for timed effects } diff --git a/src/lib/game/utils.ts b/src/lib/game/utils.ts index 3345c5b..235f60c 100755 --- a/src/lib/game/utils.ts +++ b/src/lib/game/utils.ts @@ -41,6 +41,21 @@ export function getFloorElement(floor: number): string { return FLOOR_ELEM_CYCLE[(floor - 1) % 8]; } +// Get floor armor (flat damage reduction) +// Higher floors have more armor to make them harder to damage +// Guardian floors have significantly more armor +export function getFloorArmor(floor: number): number { + const isGuardianFloor = !!GUARDIANS[floor]; + + // Base armor scales with floor + // Non-guardian: 0-10 armor (linear scaling) + // Guardian: 5-50 armor (much higher) + if (isGuardianFloor) { + return Math.floor(5 + floor * 0.5); + } + return Math.floor(floor * 0.1); +} + // ─── Computed Stats Functions ───────────────────────────────────────────────── export function computeMaxMana( @@ -227,13 +242,13 @@ export function getBoonBonuses(signedPacts: number[]): { // Each guardian grants a unique perk when pact is signed export interface GuardianPerks { fireSpellSpeed: number; // Floor 10: Fire spells cast 10% faster - waterLifesteal: number; // Floor 20: Water spells have 10% lifesteal + waterDoubleCast: number; // Floor 20: Water spells have 10% chance to cast twice airCritChance: number; // Floor 30: Air spells have 15% crit chance earthGuardianDmg: number; // Floor 40: Earth spells deal +25% damage to guardians lightDmgBonus: number; // Floor 50: Light spells deal +20% damage - darkLifesteal: number; // Floor 60: Dark spells have 20% lifesteal - lifeHealRatio: number; // Floor 70: Life spells heal 30% of damage - deathExecute: number; // Floor 80: Death spells execute below 20% HP + darkCritChance: number; // Floor 60: Dark spells have 25% crit chance + lifeManaRestore: number; // Floor 70: Life spells restore 30% of damage as mana + deathLowHpDmg: number; // Floor 80: Death spells deal +50% damage to enemies below 50% HP voidPierce: number; // Floor 90: Void spells ignore 30% resistance stellarAllDmg: number; // Floor 100: All spells deal +50% damage stellarSpeed: number; // Floor 100: All spells cast 25% faster @@ -242,13 +257,13 @@ export interface GuardianPerks { export function getGuardianPerks(signedPacts: number[]): GuardianPerks { const perks: GuardianPerks = { fireSpellSpeed: 0, - waterLifesteal: 0, + waterDoubleCast: 0, airCritChance: 0, earthGuardianDmg: 0, lightDmgBonus: 0, - darkLifesteal: 0, - lifeHealRatio: 0, - deathExecute: 0, + darkCritChance: 0, + lifeManaRestore: 0, + deathLowHpDmg: 0, voidPierce: 0, stellarAllDmg: 0, stellarSpeed: 0, @@ -257,13 +272,13 @@ export function getGuardianPerks(signedPacts: number[]): GuardianPerks { for (const floor of signedPacts) { switch (floor) { case 10: perks.fireSpellSpeed = 0.10; break; // Fire spells cast 10% faster - case 20: perks.waterLifesteal = 0.10; break; // Water spells have 10% lifesteal + case 20: perks.waterDoubleCast = 0.10; break; // Water spells 10% chance to cast twice case 30: perks.airCritChance = 0.15; break; // Air spells have 15% crit chance case 40: perks.earthGuardianDmg = 0.25; break; // Earth spells +25% dmg to guardians case 50: perks.lightDmgBonus = 0.20; break; // Light spells +20% damage - case 60: perks.darkLifesteal = 0.20; break; // Dark spells have 20% lifesteal - case 70: perks.lifeHealRatio = 0.30; break; // Life spells heal 30% of damage - case 80: perks.deathExecute = 0.20; break; // Death spells execute below 20% HP + case 60: perks.darkCritChance = 0.25; break; // Dark spells have 25% crit chance + case 70: perks.lifeManaRestore = 0.30; break; // Life spells restore 30% dmg as mana + case 80: perks.deathLowHpDmg = 0.50; break; // Death spells +50% dmg to low HP case 90: perks.voidPierce = 0.30; break; // Void spells ignore 30% resistance case 100: perks.stellarAllDmg = 0.50; // All spells +50% damage