/** * MagicSystem.js * ============== * Spell casting, magic staffs, potions, and elemental effects * * Features: * - 6 elemental staffs (fire, ice, lightning, earth, light, dark) * - Spell system with mana costs * - Potion brewing and consumption * - Elemental damage types * - Magic projectiles * - Buff/debuff effects * - Spell combos * * Uses: portal_states_broken_repaired_1766097098724.tsx (magic portals) * * @author NovaFarma Team * @date 2025-12-22 */ export default class MagicSystem { constructor(scene) { this.scene = scene; // Player magic state this.mana = 100; this.maxMana = 100; this.manaRegenRate = 5; // per second // Equipment this.equippedStaff = null; this.equippedSpells = new Map(); // slot -> spell // Spell databases this.spells = new Map(); this.staffs = new Map(); this.potions = new Map(); // Active effects this.activeEffects = []; this.activeProjectiles = []; // Cooldowns this.spellCooldowns = new Map(); // Initialize databases this.initializeSpells(); this.initializeStaffs(); this.initializePotions(); // Load saved data this.loadMagicData(); console.log('✨ MagicSystem initialized'); } initializeSpells() { // ===== FIRE MAGIC ===== this.addSpell('fireball', { name: 'Fireball', element: 'fire', manaCost: 20, damage: 50, range: 200, speed: 300, cooldown: 2000, requiresStaff: 'fire', description: 'Launch a ball of fire', effect: 'burn' }); this.addSpell('fire_blast', { name: 'Fire Blast', element: 'fire', manaCost: 40, damage: 100, range: 150, aoe: 80, cooldown: 5000, requiresStaff: 'fire', description: 'Area explosion of flames', effect: 'burn' }); // ===== ICE MAGIC ===== this.addSpell('ice_shard', { name: 'Ice Shard', element: 'ice', manaCost: 15, damage: 40, range: 250, speed: 350, cooldown: 1500, requiresStaff: 'ice', description: 'Pierce enemies with ice', effect: 'slow' }); this.addSpell('frost_nova', { name: 'Frost Nova', element: 'ice', manaCost: 50, damage: 60, aoe: 120, cooldown: 6000, requiresStaff: 'ice', description: 'Freeze everything nearby', effect: 'freeze' }); // ===== LIGHTNING MAGIC ===== this.addSpell('lightning_bolt', { name: 'Lightning Bolt', element: 'lightning', manaCost: 25, damage: 70, range: 300, speed: 500, // Fast! cooldown: 2500, requiresStaff: 'lightning', description: 'Strike with lightning', effect: 'stun' }); this.addSpell('chain_lightning', { name: 'Chain Lightning', element: 'lightning', manaCost: 60, damage: 45, range: 200, chains: 5, // Hits 5 targets cooldown: 7000, requiresStaff: 'lightning', description: 'Lightning jumps between enemies' }); // ===== EARTH MAGIC ===== this.addSpell('stone_spike', { name: 'Stone Spike', element: 'earth', manaCost: 30, damage: 80, range: 150, cooldown: 3000, requiresStaff: 'earth', description: 'Erupt spikes from the ground' }); this.addSpell('earth_shield', { name: 'Earth Shield', element: 'earth', manaCost: 40, defense: 50, // Damage reduction duration: 10000, // 10 seconds cooldown: 15000, requiresStaff: 'earth', description: 'Summon protective barrier', isBuff: true }); // ===== LIGHT MAGIC ===== this.addSpell('heal', { name: 'Healing Light', element: 'light', manaCost: 35, healing: 50, cooldown: 4000, requiresStaff: 'light', description: 'Restore health', isHeal: true }); this.addSpell('holy_smite', { name: 'Holy Smite', element: 'light', manaCost: 45, damage: 90, range: 180, effectiveVs: ['undead', 'dark'], cooldown: 5000, requiresStaff: 'light', description: 'Devastating against undead' }); // ===== DARK MAGIC ===== this.addSpell('shadow_bolt', { name: 'Shadow Bolt', element: 'dark', manaCost: 20, damage: 55, range: 220, speed: 280, lifeSteal: 0.3, // 30% damage as healing cooldown: 2000, requiresStaff: 'dark', description: 'Drain life from enemies' }); this.addSpell('curse', { name: 'Curse of Weakness', element: 'dark', manaCost: 40, debuff: { attack: 0.5, defense: 0.5 }, // 50% reduction duration: 15000, cooldown: 8000, requiresStaff: 'dark', description: 'Weaken enemy significantly', isDebuff: true }); } initializeStaffs() { this.addStaff('fire', { name: 'Fire Staff', element: 'fire', manaBonus: 20, spellPowerBonus: 1.2, // 20% more damage unlockLevel: 10, sprite: 'magic_staffs_6_types', // Frame 0 cost: 5000, blueprint: 'blueprint_fire_staff' }); this.addStaff('ice', { name: 'Ice Staff', element: 'ice', manaBonus: 20, spellPowerBonus: 1.2, unlockLevel: 10, sprite: 'magic_staffs_6_types', // Frame 1 cost: 5000, blueprint: 'blueprint_ice_staff' }); this.addStaff('lightning', { name: 'Lightning Staff', element: 'lightning', manaBonus: 20, spellPowerBonus: 1.2, unlockLevel: 12, sprite: 'magic_staffs_6_types', // Frame 2 cost: 6000, blueprint: 'blueprint_lightning_staff' }); this.addStaff('earth', { name: 'Earth Staff', element: 'earth', manaBonus: 30, spellPowerBonus: 1.1, defenseBonus: 10, unlockLevel: 11, sprite: 'magic_staffs_6_types', // Frame 3 cost: 5500, blueprint: 'blueprint_earth_staff' }); this.addStaff('light', { name: 'Light Staff', element: 'light', manaBonus: 25, spellPowerBonus: 1.15, healingBonus: 1.3, // 30% more healing unlockLevel: 13, sprite: 'magic_staffs_6_types', // Frame 4 cost: 7000, blueprint: 'blueprint_light_staff' }); this.addStaff('dark', { name: 'Dark Staff', element: 'dark', manaBonus: 20, spellPowerBonus: 1.25, // Highest damage lifeStealBonus: 0.1, // Extra 10% lifesteal unlockLevel: 14, sprite: 'magic_staffs_6_types', // Frame 5 cost: 8000, blueprint: 'blueprint_dark_staff' }); } initializePotions() { this.addPotion('mana_potion', { name: 'Mana Potion', manaRestore: 50, cooldown: 30000, // 30 second potion cooldown cost: 50, sprite: 'potions_elixirs_grid', // Frame 0 description: 'Restore 50 mana' }); this.addPotion('greater_mana_potion', { name: 'Greater Mana Potion', manaRestore: 100, cooldown: 30000, cost: 150, sprite: 'potions_elixirs_grid', // Frame 1 description: 'Restore 100 mana' }); this.addPotion('magic_power_elixir', { name: 'Magic Power Elixir', spellPowerBuff: 1.5, // 50% more spell damage duration: 60000, // 1 minute cooldown: 120000, // 2 minute cooldown cost: 300, sprite: 'potions_elixirs_grid', // Frame 2 description: 'Greatly increase spell power' }); } addSpell(id, data) { this.spells.set(id, { id, ...data }); } addStaff(id, data) { this.staffs.set(id, { id, ...data }); } addPotion(id, data) { this.potions.set(id, { id, ...data }); } // ===== SPELL CASTING ===== canCastSpell(spellId) { const spell = this.spells.get(spellId); if (!spell) { return { canCast: false, reason: 'Spell not found' }; } // Check mana if (this.mana < spell.manaCost) { return { canCast: false, reason: `Need ${spell.manaCost} mana (have ${this.mana.toFixed(0)})` }; } // Check staff requirement if (spell.requiresStaff) { if (!this.equippedStaff || this.equippedStaff.element !== spell.requiresStaff) { return { canCast: false, reason: `Requires ${spell.requiresStaff} staff` }; } } // Check cooldown if (this.spellCooldowns.has(spellId)) { const remaining = this.spellCooldowns.get(spellId) - Date.now(); if (remaining > 0) { return { canCast: false, reason: `Cooldown: ${(remaining / 1000).toFixed(1)}s` }; } } return { canCast: true }; } castSpell(spellId, targetX, targetY) { const check = this.canCastSpell(spellId); if (!check.canCast) { this.scene.uiSystem?.showError(check.reason); return false; } const spell = this.spells.get(spellId); // Consume mana this.mana -= spell.manaCost; // Apply cooldown this.spellCooldowns.set(spellId, Date.now() + spell.cooldown); // Calculate spell power const spellPower = this.getSpellPower(spell); // Execute spell effect if (spell.isHeal) { this.castHealSpell(spell, spellPower); } else if (spell.isBuff) { this.castBuffSpell(spell); } else if (spell.isDebuff) { this.castDebuffSpell(spell, targetX, targetY); } else if (spell.aoe) { this.castAoESpell(spell, targetX, targetY, spellPower); } else { this.castProjectileSpell(spell, targetX, targetY, spellPower); } // Emit event this.scene.events.emit('spellCast', { spellId, spell, targetX, targetY }); console.log(`✨ Cast ${spell.name} (${spell.manaCost} mana)`); return true; } castProjectileSpell(spell, targetX, targetY, power) { const playerX = this.scene.player.x; const playerY = this.scene.player.y; // Create projectile sprite const projectile = this.scene.physics.add.sprite( playerX, playerY, `spell_${spell.element}` ); projectile.setData('spell', spell); projectile.setData('damage', spell.damage * power); // Aim at target this.scene.physics.moveTo(projectile, targetX, targetY, spell.speed); // Add particle trail if (this.scene.particleSystem) { this.scene.particleSystem.createSpellTrail(projectile, spell.element); } // Handle collision with enemies this.scene.physics.add.overlap(projectile, this.scene.enemies, (proj, enemy) => { this.hitEnemy(enemy, proj.getData('damage'), spell); proj.destroy(); }); // Auto-destroy after range this.scene.time.delayedCall(spell.range / spell.speed * 1000, () => { if (projectile && projectile.active) { projectile.destroy(); } }); this.activeProjectiles.push(projectile); } castAoESpell(spell, targetX, targetY, power) { // Create explosion effect if (this.scene.particleSystem) { this.scene.particleSystem.createSpellExplosion(targetX, targetY, spell.element, spell.aoe); } // Damage all enemies in radius this.scene.enemies?.getChildren().forEach(enemy => { const distance = Phaser.Math.Distance.Between( targetX, targetY, enemy.x, enemy.y ); if (distance <= spell.aoe) { this.hitEnemy(enemy, spell.damage * power, spell); } }); } castHealSpell(spell, power) { const healing = spell.healing * power; // Heal player if (this.scene.player.health !== undefined) { this.scene.player.health = Math.min( this.scene.player.maxHealth || 100, this.scene.player.health + healing ); } // Visual effect if (this.scene.particleSystem) { this.scene.particleSystem.createHealEffect( this.scene.player.x, this.scene.player.y ); } this.scene.events.emit('playerHealed', { amount: healing }); } castBuffSpell(spell) { this.activeEffects.push({ spell, endTime: Date.now() + spell.duration, type: 'buff' }); this.scene.uiSystem?.showNotification({ title: `${spell.name} Active!`, message: `Defense +${spell.defense} for ${spell.duration / 1000}s`, icon: 'magic', duration: 3000 }); } castDebuffSpell(spell, targetX, targetY) { // Find nearest enemy const enemy = this.findNearestEnemy(targetX, targetY); if (enemy) { enemy.setData('debuff', { spell, endTime: Date.now() + spell.duration }); // Visual effect if (this.scene.particleSystem) { this.scene.particleSystem.createDebuffEffect(enemy.x, enemy.y); } } } hitEnemy(enemy, damage, spell) { // Apply damage const finalDamage = damage; enemy.takeDamage?.(finalDamage); // Apply effect if (spell.effect) { this.applyEffect(enemy, spell.effect, spell); } // Lifesteal if (spell.lifeSteal && this.scene.player.health !== undefined) { const healing = finalDamage * spell.lifeSteal; this.scene.player.health = Math.min( this.scene.player.maxHealth || 100, this.scene.player.health + healing ); } } applyEffect(target, effectType, spell) { switch (effectType) { case 'burn': // Damage over time target.setData('burning', { duration: 3000, dps: 10 }); break; case 'slow': // Reduce speed target.setData('slowed', { duration: 2000, factor: 0.5 }); break; case 'freeze': // Stop movement target.setData('frozen', { duration: 1500 }); target.setVelocity(0, 0); break; case 'stun': // Cannot act target.setData('stunned', { duration: 1000 }); break; } } // ===== STAFF MANAGEMENT ===== equipStaff(staffId) { const staff = this.staffs.get(staffId); if (!staff) return false; this.equippedStaff = staff; // Apply bonuses this.maxMana = 100 + (staff.manaBonus || 0); this.scene.uiSystem?.showNotification({ title: `Equipped ${staff.name}`, message: `+${staff.manaBonus} max mana`, icon: 'magic', duration: 2000 }); return true; } getSpellPower(spell) { let power = 1.0; // Staff bonus if (this.equippedStaff) { power *= this.equippedStaff.spellPowerBonus || 1.0; // Healing bonus if (spell.isHeal && this.equippedStaff.healingBonus) { power *= this.equippedStaff.healingBonus; } } // Active potion buffs this.activeEffects.forEach(effect => { if (effect.spell.spellPowerBuff) { power *= effect.spell.spellPowerBuff; } }); return power; } // ===== POTION SYSTEM ===== drinkPotion(potionId) { const potion = this.potions.get(potionId); if (!potion) return false; // Check cooldown if (this.spellCooldowns.has(`potion_${potionId}`)) { const remaining = this.spellCooldowns.get(`potion_${potionId}`) - Date.now(); if (remaining > 0) { this.scene.uiSystem?.showError(`Potion cooldown: ${(remaining / 1000).toFixed(0)}s`); return false; } } // Restore mana if (potion.manaRestore) { this.mana = Math.min(this.maxMana, this.mana + potion.manaRestore); } // Apply buff if (potion.spellPowerBuff) { this.activeEffects.push({ spell: potion, endTime: Date.now() + potion.duration, type: 'potion_buff' }); } // Set cooldown this.spellCooldowns.set(`potion_${potionId}`, Date.now() + potion.cooldown); console.log(`🧪 Drank ${potion.name}`); return true; } // ===== HELPER FUNCTIONS ===== findNearestEnemy(x, y) { let nearest = null; let minDist = Infinity; this.scene.enemies?.getChildren().forEach(enemy => { const dist = Phaser.Math.Distance.Between(x, y, enemy.x, enemy.y); if (dist < minDist) { minDist = dist; nearest = enemy; } }); return nearest; } // ===== DATA PERSISTENCE ===== saveMagicData() { const data = { mana: this.mana, maxMana: this.maxMana, equippedStaff: this.equippedStaff?.id }; localStorage.setItem('magicData', JSON.stringify(data)); } loadMagicData() { const saved = localStorage.getItem('magicData'); if (saved) { const data = JSON.parse(saved); this.mana = data.mana; this.maxMana = data.maxMana; if (data.equippedStaff) { this.equipStaff(data.equippedStaff); } console.log('✨ Loaded magic data'); } } // ===== UPDATE ===== update(time, delta) { // Regenerate mana const deltaSeconds = delta / 1000; this.mana = Math.min(this.maxMana, this.mana + this.manaRegenRate * deltaSeconds); // Update active effects const now = Date.now(); this.activeEffects = this.activeEffects.filter(effect => effect.endTime > now); // Clean up destroyed projectiles this.activeProjectiles = this.activeProjectiles.filter(p => p.active); // Emit mana update this.scene.events.emit('manaUpdate', { current: this.mana, max: this.maxMana, percent: (this.mana / this.maxMana) * 100 }); } // ===== CLEANUP ===== destroy() { this.saveMagicData(); this.spells.clear(); this.staffs.clear(); this.potions.clear(); this.activeEffects = []; this.activeProjectiles = []; this.spellCooldowns.clear(); } }