diff --git a/sql/updates/world/2024_11_21_fix_mage_mirror_image.sql b/sql/updates/world/2024_11_21_fix_mage_mirror_image.sql new file mode 100644 index 00000000..bf4183bb --- /dev/null +++ b/sql/updates/world/2024_11_21_fix_mage_mirror_image.sql @@ -0,0 +1,5 @@ +-- +DELETE FROM `spell_linked_spell` WHERE `spell_trigger` = 55342; + +DELETE FROM `spell_script_names` WHERE `spell_id` = 55342; +INSERT INTO `spell_script_names` (spell_id, ScriptName) VALUES (55342, 'spell_mage_mirror_image_summon'); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index b86cc156..d47e33cb 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2084,6 +2084,10 @@ void AuraEffect::UpdatePeriodic(Unit* caster) break; } break; + case SPELLFAMILY_MAGE: + if (GetId() == 55342) // mirror image + m_isPeriodic = false; + break; default: break; } @@ -7244,6 +7248,14 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster, SpellEf } break; } + case SPELLFAMILY_MAGE: + { + // Mirror Image + if (GetId() == 55342) + // Set name of summons to name of caster + caster->CastSpell((Unit*)nullptr, GetSpellEffectInfo()->TriggerSpell, true); + break; + } default: break; } diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index a8a7c222..65d061f0 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -29,6 +29,175 @@ //#include "AreaTriggerAI.h" //#include "AreaTrigger.h" +enum MageSpells +{ + //7.3.2.25549 + SPELL_MAGE_COLD_SNAP = 235219, + SPELL_MAGE_FROST_NOVA = 122, + SPELL_MAGE_CONE_OF_COLD = 120, + SPELL_MAGE_CONE_OF_COLD_SLOW = 212792, + SPELL_MAGE_ICE_BARRIER = 11426, + SPELL_MAGE_ICE_BLOCK = 45438, + SPELL_MAGE_GLACIAL_INSULATION = 235297, + SPELL_MAGE_BONE_CHILLING = 205027, + SPELL_MAGE_BONE_CHILLING_BUFF = 205766, + SPELL_MAGE_CHILLED = 205708, + SPELL_MAGE_ICE_LANCE = 30455, + SPELL_MAGE_ICE_LANCE_TRIGGER = 228598, + SPELL_MAGE_THERMAL_VOID = 155149, + SPELL_MAGE_ICY_VEINS = 12472, + SPELL_MAGE_GLACIAL_SPIKE = 199786, + SPELL_MAGE_ICICLE_PERIODIC_TRIGGER = 148023, + SPELL_MAGE_FLURRY_DEBUFF_PROC = 228354, + SPELL_MAGE_FLURRY = 44614, + SPELL_MAGE_FLURRY_DAMAGE = 228672, + SPELL_MAGE_FLURRY_CHILL_PROC = 228358, + SPELL_MAGE_FLURRY_VISUAL = 228596, + SPELL_MAGE_SHIELD_OF_ALODI = 195354, + SPELL_MAGE_BRAIN_FREEZE = 190447, + SPELL_MAGE_BRAIN_FREEZE_AURA = 190446, + SPELL_MAGE_BRAIN_FREEZE_IMPROVED = 231584, + SPELL_MAGE_JOUSTER = 214626, + SPELL_MAGE_CHAIN_REACTION = 195419, + SPELL_MAGE_CHILLED_TO_THE_CORE = 195448, + SPELL_MAGE_GLARITY_OF_THOUGHT = 195351, + SPELL_MAGE_ICE_NOVA = 157997, + SPELL_MAGE_FROZEN_TOUCH = 205030, + SPELL_MAGE_FROZEN_ORB = 84714, + SPELL_MAGE_FROZEN_ORB_DAMAGE = 84721, + SPELL_MAGE_BLIZZARD_RANK_2 = 236662, + SPELL_MAGE_UNSTABLE_MAGIC = 157976, + SPELL_MAGE_UNSTABLE_MAGIC_DAMAGE_FIRE = 157977, + SPELL_MAGE_UNSTABLE_MAGIC_DAMAGE_FROST = 157978, + SPELL_MAGE_UNSTABLE_MAGIC_DAMAGE_ARCANE = 157979, + SPELL_MAGE_FINGERS_OF_FROST = 112965, + SPELL_MAGE_FINGERS_OF_FROST_AURA = 44544, + SPELL_MAGE_FINGERS_OF_FROST_VISUAL_UI = 126084, + SPELL_MAGE_FROST_BOMB_AURA = 112948, + SPELL_MAGE_FROST_BOMB_TRIGGERED = 113092, + SPELL_MAGE_FROSTBOLT = 116, + SPELL_MAGE_FROSTBOLT_TRIGGER = 228597, + SPELL_MAGE_RAY_OF_FROST = 205021, + SPELL_MAGE_RAY_OF_FROST_BUFF = 208166, + SPELL_MAGE_RAY_OF_FROST_DAMAGE_INCREASE = 208141, + SPELL_BLAZING_BARRIER_TRIGGER = 235314, + SPELL_MAGE_SCORCH = 2948, + SPELL_MAGE_FIREBALL = 133, + SPELL_MAGE_FIRE_BLAST = 108853, + SPELL_MAGE_FLAMESTRIKE = 2120, + SPELL_MAGE_PYROBLAST = 11366, + SPELL_MAGE_PHOENIX_FLAMES = 194466, + SPELL_MAGE_DRAGON_BREATH = 31661, + SPELL_MAGE_PYROMANIAC = 205020, + SPELL_MAGE_ALEXSTRASZAS_FURY = 235870, + SPELL_MAGE_LIVING_BOMB_DAMAGE = 44461, + SPELL_MAGE_LIVING_BOMB_DOT = 217694, + SPELL_MAGE_METEOR_DAMAGE = 153564, + SPELL_MAGE_METEOR_TIMER = 177345, + SPELL_MAGE_METEOR_VISUAL = 174556, + SPELL_MAGE_METEOR_BURN = 155158, + SPELL_MAGE_COMET_STORM = 153595, + SPELL_MAGE_COMET_STORM_DAMAGE = 153596, + SPELL_MAGE_COMET_STORM_VISUAL = 242210, + SPELL_MAGE_POLYMORPH_CRITTERMORPH = 120091, + SPELL_MAGE_HEATING_UP = 48107, + SPELL_MAGE_HOT_STREAK = 48108, + SPELL_MAGE_ENHANCED_PYROTECHNICS_AURA = 157644, + + SPELL_MAGE_INCANTERS_FLOW_BUFF = 116267, + SPELL_MAGE_RUNE_OF_POWER_BUFF = 116014, + SPELL_MAGE_OVERPOWERED = 155147, + SPELL_MAGE_ARCANE_POWER = 12042, + SPELL_MAGE_CHRONO_SHIFT = 235711, + SPELL_MAGE_CHRONO_SHIFT_SLOW = 236299, + SPELL_MAGE_ARCANE_BLAST = 30451, + SPELL_MAGE_ARCANE_BARRAGE = 44425, + SPELL_MAGE_ARCANE_BARRAGE_TRIGGERED = 241241, + SPELL_MAGE_PRESENCE_OF_MIND = 205025, + SPELL_MAGE_ARCANE_MISSILES_VISUAL_TWO = 79808, + SPELL_MAGE_ARCANE_MISSILES_VISUAL_ONE = 170571, + SPELL_MAGE_ARCANE_MISSILES_VISUAL_THREE = 170572, + SPELL_MAGE_ARCANE_MISSILES_TRIGGER = 7268, + SPELL_MAGE_ARCANE_MISSILES = 5143, + SPELL_MAGE_ARCANE_MISSILES_DAMAGE = 7268, + SPELL_MAGE_ARCANE_MISSILES_POWER = 208030, + SPELL_MAGE_ARCANE_MISSILES_CHARGES = 79683, + SPELL_MAGE_ARCANE_ORB_DAMAGE = 153640, + SPELL_MAGE_ARCANE_AMPLIFICATION = 236628, + + //7.3.2.25549 END + SPELL_MAGE_RING_OF_FROST_FREEZE = 82691, + SPELL_MAGE_RING_OF_FROST_IMMUNE = 91264, + SPELL_MAGE_RING_OF_FROST = 113724, + SPELL_MAGE_FIRE_MAGE_PASSIVE = 137019, + SPELL_MAGE_FIRE_ON = 205029, + SPELL_MAGE_FIRESTARTER = 205026, + SPELL_MAGE_CAUTERIZE = 87023, + SPELL_MAGE_MIRROR_IMAGE_LEFT = 58834, + SPELL_MAGE_MIRROR_IMAGE_RIGHT = 58833, + SPELL_MAGE_MIRROR_IMAGE_FRONT = 58831, + SPELL_MAGE_COMBUSTION = 190319, + SPELL_MAGE_WATER_JET = 135029, + SPELL_MAGE_ICE_FLOES = 108839, + SPELL_MAGE_CONJURE_REFRESHMENT_GROUP = 167145, + SPELL_MAGE_CONJURE_REFRESHMENT_SOLO = 116136, + SPELL_MAGE_HYPOTHERMIA = 41425, + SPELL_INFERNO = 253220, + SPELL_MAGE_BLAZING_BARRIER = 235313, + SPELL_MAGE_BLAZING_SOUL = 235365, + SPELL_MAGE_CONTROLLED_BURN = 205033, + SPELL_MAGE_FLAME_PATCH = 205037, + SPELL_MAGE_FLAME_PATCH_TRIGGER = 205470, + SPELL_MAGE_FLAME_PATCH_AOE_DMG = 205472, + SPELL_MAGE_CINDERSTORM = 198929, + SPELL_MAGE_CINDERSTORM_DMG = 198928, + SPELL_MAGE_IGNITE_DOT = 12654, + + SPELL_MAGE_SQUIRREL_FORM = 32813, + SPELL_MAGE_GIRAFFE_FORM = 32816, + SPELL_MAGE_SERPENT_FORM = 32817, + SPELL_MAGE_DRAGONHAWK_FORM = 32818, + SPELL_MAGE_WORGEN_FORM = 32819, + SPELL_MAGE_SHEEP_FORM = 32820, + + SPELL_MAGE_MASTERY_ICICLES = 76613, + SPELL_MAGE_ICICLE_DAMAGE = 148022, + SPELL_MAGE_ICICLE_AURA = 205473, + SPELL_MAGE_SPLITTING_ICE = 56377 +}; + +// Mirror Image - 55342 +class spell_mage_mirror_image_summon : public SpellScriptLoader +{ +public: + spell_mage_mirror_image_summon() : SpellScriptLoader("spell_mage_mirror_image_summon") { } + + class spell_mage_mirror_image_summon_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mage_mirror_image_summon_SpellScript); + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + { + caster->CastSpell(caster, SPELL_MAGE_MIRROR_IMAGE_LEFT, true); + caster->CastSpell(caster, SPELL_MAGE_MIRROR_IMAGE_FRONT, true); + caster->CastSpell(caster, SPELL_MAGE_MIRROR_IMAGE_RIGHT, true); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mage_mirror_image_summon_SpellScript::HandleDummy, EFFECT_1, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mage_mirror_image_summon_SpellScript(); + } +}; + // Cauterize - 86949 class spell_mage_cauterize : public SpellScriptLoader { @@ -441,21 +610,6 @@ const uint32 icicles[5][3] {148016, 148021, 148012} }; -enum MageSpells -{ - SPELL_MAGE_SQUIRREL_FORM = 32813, - SPELL_MAGE_GIRAFFE_FORM = 32816, - SPELL_MAGE_SERPENT_FORM = 32817, - SPELL_MAGE_DRAGONHAWK_FORM = 32818, - SPELL_MAGE_WORGEN_FORM = 32819, - SPELL_MAGE_SHEEP_FORM = 32820, - - SplittingIce = 56377, - IciclesStack = 205473, - IciclesDamage = 148022, - MasteryIcicles = 76613 -}; - enum SilvermoonPolymorph { NPC_AUROSALIA = 18744, @@ -666,7 +820,7 @@ class spell_mage_icicle : public AuraScript { if (Unit* caster = GetCaster()) { - if (Aura* aura = caster->GetAura(MasteryIcicles)) + if (Aura* aura = caster->GetAura(SPELL_MAGE_MASTERY_ICICLES)) { for (auto itr : aura->m_loadedScripts) { @@ -683,7 +837,7 @@ class spell_mage_icicle : public AuraScript if (icicles[i][0] == spellId) { caster->CastSpell(target, icicles[i][1], true); - caster->CastCustomSpell(target, IciclesDamage, &tickamount, NULL, NULL, true); + caster->CastCustomSpell(target, SPELL_MAGE_ICICLE_DAMAGE, &tickamount, NULL, NULL, true); caster->RemoveAurasDueToSpell(spellId); return; } @@ -1185,7 +1339,7 @@ class spell_mage_ice_lance_main : public SpellScript { Trinity::AnyDataContainer& cont = caster->GetAnyDataContainer(); - if (caster->HasAura(MasteryIcicles) && !caster->HasSpell(199786)) // Mastery: Icicles, Glacial Spike + if (caster->HasAura(SPELL_MAGE_MASTERY_ICICLES) && !caster->HasSpell(199786)) // Mastery: Icicles, Glacial Spike { if (Unit* target = GetOriginalTarget()) { @@ -1205,7 +1359,7 @@ class spell_mage_ice_lance_main : public SpellScript cont.Set("isFrozenTarget", 0); } - if (caster->HasAura(SplittingIce)) + if (caster->HasAura(SPELL_MAGE_SPLITTING_ICE)) { if (Spell* spell = GetSpell()) { @@ -1273,7 +1427,7 @@ class spell_mage_ice_lance : public SpellScript { if (target->GetGUID() == jumpTargetGUID) { - if (SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(SplittingIce)) + if (SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(SPELL_MAGE_SPLITTING_ICE)) dmgMod = CalculatePct(dmgMod, spellinfo->Effects[EFFECT_1]->BasePoints); } } @@ -1734,7 +1888,7 @@ class spell_mage_mastery_icicles: public AuraScript if (AuraEffect const* eff = caster->GetAuraEffect(spellId, EFFECT_0)) { float bp = eff->GetAmount(); - caster->CastCustomSpell(target, IciclesDamage, &bp, NULL, NULL, true); + caster->CastCustomSpell(target, SPELL_MAGE_ICICLE_DAMAGE, &bp, NULL, NULL, true); for (uint8 i = 0; i < 5; i++) { @@ -1770,7 +1924,7 @@ class spell_mage_mastery_icicles: public AuraScript void Cast(Unit* caster, uint32 const& spellId, float& damage) { - caster->CastSpell(caster, IciclesStack, true); + caster->CastSpell(caster, SPELL_MAGE_ICICLE_AURA, true); caster->CastCustomSpell(caster, spellId, &damage, NULL, NULL, true); getIcicles.push_back(spellId); } @@ -2000,6 +2154,7 @@ class spell_mage_phoenixs_flames : public SpellScript void AddSC_mage_spell_scripts() { + new spell_mage_mirror_image_summon(); new spell_mage_cauterize(); new spell_mage_conjure_refreshment(); new spell_mage_time_warp(); diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index 5e03b6eb..95485019 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -3160,39 +3160,160 @@ class npc_ebon_gargoyle : public CreatureScript } }; +// 31216 - Mirror Image class npc_mage_mirror_image : public CreatureScript { public: npc_mage_mirror_image() : CreatureScript("npc_mage_mirror_image") { } + enum eSpells + { + SPELL_MAGE_FROSTBOLT = 59638, + SPELL_MAGE_FIREBALL = 133, + SPELL_MAGE_ARCANE_BLAST = 30451, + SPELL_MAGE_GLYPH = 63093, + SPELL_INITIALIZE_IMAGES = 102284, + SPELL_CLONE_CASTER = 60352, + SPELL_INHERIT_MASTER_THREAT = 58838 + }; + struct npc_mage_mirror_imageAI : CasterAI { - npc_mage_mirror_imageAI(Creature* creature) : CasterAI(creature) {} + npc_mage_mirror_imageAI(Creature* creature) : CasterAI(creature) { } - uint32 targetCheckTime; - bool firstCheck; + void IsSummonedBy(Unit* owner) override + { + if (!owner || !owner->IsPlayer()) + return; - void InitializeAI() override + if (!me->HasUnitState(UnitState::UNIT_STATE_FOLLOW)) + { + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MovementSlot::MOTION_SLOT_ACTIVE); + } + + me->SetMaxPower(me->getPowerType(), owner->GetMaxPower(me->getPowerType())); + me->SetPower(me->getPowerType(), me->GetMaxPower(me->getPowerType())); + me->SetMaxHealth(owner->GetMaxHealth()); + me->SetHealth(owner->GetHealth()); + me->SetReactState(ReactStates::REACT_DEFENSIVE); + + me->CastSpell(owner, SPELL_INHERIT_MASTER_THREAT, true); + + // here mirror image casts on summoner spell (not present in client dbc) 49866 + // here should be auras (not present in client dbc): 35657, 35658, 35659, 35660 selfcasted by mirror images (stats related?) + + for (uint32 attackType = 0; attackType < WeaponAttackType::MAX_ATTACK; ++attackType) + { + WeaponAttackType attackTypeEnum = static_cast(attackType); + me->SetBaseWeaponDamage(attackTypeEnum, WeaponDamageRange::MAXDAMAGE, owner->GetWeaponDamageRange(attackTypeEnum, WeaponDamageRange::MAXDAMAGE)); + me->SetBaseWeaponDamage(attackTypeEnum, WeaponDamageRange::MINDAMAGE, owner->GetWeaponDamageRange(attackTypeEnum, WeaponDamageRange::MINDAMAGE)); + } + + me->UpdateAttackPowerAndDamage(); + } + + void EnterCombat(Unit* /*who*/) override { - CasterAI::InitializeAI(); - targetCheckTime = 0; - firstCheck = false; + if (!me->GetOwner()) + return; + + Player* owner = me->GetOwner()->ToPlayer(); + if (!owner) + return; + + eSpells spellId = eSpells::SPELL_MAGE_FROSTBOLT; + /*if (owner->HasAura(SPELL_MAGE_GLYPH)) + {*/ + switch (owner->GetSpecializationId()) + { + case TALENT_SPEC_MAGE_ARCANE: spellId = eSpells::SPELL_MAGE_ARCANE_BLAST; break; + case TALENT_SPEC_MAGE_FIRE: spellId = eSpells::SPELL_MAGE_FIREBALL; break; + default: break; + } + //} + + events.ScheduleEvent(spellId, 0); ///< Schedule cast + me->GetMotionMaster()->Clear(false); + } + + void EnterEvadeMode() override + { + if (me->IsInEvadeMode() || !me->IsAlive()) + return; + + Unit* owner = me->GetOwner(); + + me->CombatStop(true); + if (owner && !me->HasUnitState(UNIT_STATE_FOLLOW)) + { + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MovementSlot::MOTION_SLOT_ACTIVE); + } + } + + void Reset() override + { + if (Unit* owner = me->GetOwner()) + { + owner->CastSpell(me, SPELL_INITIALIZE_IMAGES, true); + owner->CastSpell(me, SPELL_CLONE_CASTER, true); + } + } + + bool CanAIAttack(Unit const* target) const override + { + /// Am I supposed to attack this target? (ie. do not attack polymorphed target) + return target && !target->HasAuraType(SPELL_AURA_MOD_CONFUSE); } void UpdateAI(uint32 diff) override { - targetCheckTime += diff; - if (targetCheckTime > 2000 || !firstCheck) + events.Update(diff); + + Unit* l_Victim = me->getVictim(); + if (l_Victim) { - if (Unit* owner = me->GetOwner()) - if (Unit* victim = owner->getVictim()) - me->Attack(victim, false); + if (CanAIAttack(l_Victim)) + { + /// If not already casting, cast! ("I'm a cast machine") + if (!me->HasUnitState(UNIT_STATE_CASTING)) + { + if (uint32 spellId = events.ExecuteEvent()) + { + DoCast(spellId); + uint32 castTime = me->GetCurrentSpellCastTime(spellId); + events.ScheduleEvent(spellId, (castTime ? castTime : 500) + sSpellMgr->GetSpellInfo(spellId)->GetAuraOptions(me->GetSpawnMode())->ProcCategoryRecovery); + } + } + } + else + { + /// My victim has changed state, I shouldn't attack it anymore + if (me->HasUnitState(UNIT_STATE_CASTING)) + me->CastStop(); - firstCheck = true; - targetCheckTime = 0; + me->AI()->EnterEvadeMode(); + } } + else + { + /// Let's choose a new target + Unit* target = me->SelectVictim(); + if (!target) + { + /// No target? Let's see if our owner has a better target for us + if (Unit* owner = me->GetOwner()) + { + Unit* ownerVictim = owner->getVictim(); + if (ownerVictim && me->canCreatureAttack(ownerVictim)) + target = ownerVictim; + } + } - CasterAI::UpdateAI(diff); + if (target) + me->AI()->AttackStart(target); + } } }; diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 764e326f..bdc36cf5 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -527,6 +527,29 @@ PersistentCharacterCleanFlags = 0 # ################################################################################################### +################################################################################################### +# SERVER LOGGING +# +# PidFile +# Description: World daemon PID file. +# Example: "./world.pid" - (Enabled) +# Default: "" - (Disabled) + +PidFile = "" + +# +# PacketLogFile +# Description: Binary packet logging file for the world server. +# Filename extension must be .pkt to be parsable with WowPacketParser. +# Example: "World.pkt" - (Enabled) +# Default: "" - (Disabled) + +PacketLogFile = "" + +# Extended Logging system configuration moved to end of file (on purpose) +# +################################################################################################### + ################################################################################################### # SERVER SETTINGS #