diff options
-rw-r--r-- | src/server/scripts/Pet/pet_mage.cpp | 216 |
1 files changed, 98 insertions, 118 deletions
diff --git a/src/server/scripts/Pet/pet_mage.cpp b/src/server/scripts/Pet/pet_mage.cpp index 06d9ce4cb49..213c578aae7 100644 --- a/src/server/scripts/Pet/pet_mage.cpp +++ b/src/server/scripts/Pet/pet_mage.cpp @@ -39,9 +39,7 @@ enum MageSpells enum MirrorImageTimers { - TIMER_MIRROR_IMAGE_INIT = 0, - TIMER_MIRROR_IMAGE_FROST_BOLT = 4000, - TIMER_MIRROR_IMAGE_FIRE_BLAST = 6000 + TIMER_MIRROR_IMAGE_FIRE_BLAST = 6500 }; class npc_pet_mage_mirror_image : public CreatureScript @@ -49,155 +47,136 @@ class npc_pet_mage_mirror_image : public CreatureScript public: npc_pet_mage_mirror_image() : CreatureScript("npc_pet_mage_mirror_image") { } - struct npc_pet_mage_mirror_imageAI : CasterAI + struct npc_pet_mage_mirror_imageAI : ScriptedAI { - npc_pet_mage_mirror_imageAI(Creature* creature) : CasterAI(creature) { } + const float CHASE_DISTANCE = 35.0f; - void Init() + npc_pet_mage_mirror_imageAI(Creature* creature) : ScriptedAI(creature) { } + + void InitializeAI() override { - Unit* owner = me->GetCharmerOrOwner(); + Unit* owner = me->GetOwner(); + if (!owner) + return; - std::list<Unit*> targets; - Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, 30.0f); - Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(me, targets, u_check); - Cell::VisitAllObjects(me, searcher, 40.0f); + // 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 selfcast by mirror images (stats related?) + // Clone Me! + owner->CastSpell(me, SPELL_MAGE_CLONE_ME, true); + } - Unit* highestThreatUnit = nullptr; - float highestThreat = 0.0f; - Unit* nearestPlayer = nullptr; - for (std::list<Unit*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) + // custom UpdateVictim implementation to handle special target selection + // we prioritize between things that are in combat with owner based on the owner's threat to them + bool UpdateVictim() + { + Unit* owner = me->GetOwner(); + if (!owner) + return false; + + if (!me->HasUnitState(UNIT_STATE_CASTING) && !me->IsInCombat() && !owner->IsInCombat()) + return false; + + Unit* currentTarget = me->GetVictim(); + if (currentTarget && !CanAIAttack(currentTarget)) { - // Consider only units without CC - if (!(*iter)->HasBreakableByDamageCrowdControlAura((*iter))) + me->InterruptNonMeleeSpells(true); // do not finish casting on invalid targets + me->AttackStop(); + currentTarget = nullptr; + } + + // don't reselect if we're currently casting anyway + if (currentTarget && me->HasUnitState(UNIT_STATE_CASTING)) + return true; + + Unit* selectedTarget = nullptr; + CombatManager const& mgr = owner->GetCombatManager(); + if (mgr.HasPvPCombat()) + { // select pvp target + float minDistance = 0.0f; + for (auto const& pair : mgr.GetPvPCombatRefs()) { - // Take first found unit - if (!highestThreatUnit && (*iter)->GetTypeId() != TYPEID_PLAYER) - { - highestThreatUnit = (*iter); + Unit* target = pair.second->GetOther(owner); + if (target->GetTypeId() != TYPEID_PLAYER) continue; - } - if (!nearestPlayer && ((*iter)->GetTypeId() == TYPEID_PLAYER)) - { - nearestPlayer = (*iter); + if (!CanAIAttack(target)) continue; - } - // else compare best fit unit with current unit - float threat = (*iter)->GetThreatManager().GetThreat(owner); - // Check if best fit hostile unit hs lower threat than this current unit - if (highestThreat < threat) - { - // If so, update best fit unit - highestThreat = threat; - highestThreatUnit = (*iter); - } - // In case no unit with threat was found so far, always check for nearest unit (only for players) - if ((*iter)->GetTypeId() == TYPEID_PLAYER) + + float dist = owner->GetDistance(target); + if (!selectedTarget || dist < minDistance) { - // If this player is closer than the previous one, update it - if (me->GetDistance((*iter)->GetPosition()) < me->GetDistance(nearestPlayer->GetPosition())) - nearestPlayer = (*iter); + selectedTarget = target; + minDistance = dist; } } } - // Prioritize units with threat referenced to owner - if (highestThreat > 0.0f && highestThreatUnit) - me->Attack(highestThreatUnit, false); - // If there is no such target, try to attack nearest hostile unit if such exists - else if (nearestPlayer) - me->Attack(nearestPlayer, false); - } - bool IsInThreatList(Unit* target) - { - Unit* owner = me->GetCharmerOrOwner(); - return owner && target->IsThreatenedBy(owner); - } - - void InitializeAI() override - { - CasterAI::InitializeAI(); - Unit* owner = me->GetOwner(); - if (!owner) - return; + if (!selectedTarget) + { // select pve target + float maxThreat = 0.0f; + for (auto const& pair : mgr.GetPvECombatRefs()) + { + Unit* target = pair.second->GetOther(owner); + if (!CanAIAttack(target)) + continue; - // 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 selfcast by mirror images (stats related?) - // Clone Me! - owner->CastSpell(me, SPELL_MAGE_CLONE_ME, false); - } + float threat = target->GetThreatManager().GetThreat(owner); + if (threat >= maxThreat) + { + selectedTarget = target; + maxThreat = threat; + } + } + } - void JustEngagedWith(Unit* who) override - { - if (me->GetVictim() && !me->GetVictim()->HasBreakableByDamageCrowdControlAura(me)) + if (!selectedTarget) { - me->CastSpell(who, SPELL_MAGE_FIRE_BLAST, false); - events.ScheduleEvent(SPELL_MAGE_FROST_BOLT, TIMER_MIRROR_IMAGE_INIT); - events.ScheduleEvent(SPELL_MAGE_FIRE_BLAST, TIMER_MIRROR_IMAGE_FIRE_BLAST); + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + return false; } - else - EnterEvadeMode(EVADE_REASON_OTHER); - } - void Reset() override - { - events.Reset(); + if (selectedTarget != me->GetVictim()) + AttackStartCaster(selectedTarget, CHASE_DISTANCE); + return true; } void UpdateAI(uint32 diff) override { - Unit* owner = me->GetCharmerOrOwner(); + Unit* owner = me->GetOwner(); if (!owner) + { + me->DespawnOrUnsummon(); return; + } - Unit* target = owner->getAttackerForHelper(); - - events.Update(diff); - - // prevent CC interrupts by images - if (me->GetVictim() && me->EnsureVictim()->HasBreakableByDamageCrowdControlAura(me)) + if (_fireBlastTimer) { - me->InterruptNonMeleeSpells(false); - return; + if (_fireBlastTimer <= diff) + _fireBlastTimer = 0; + else + _fireBlastTimer -= diff; } + if (!UpdateVictim()) + return; + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - // assign target if image doesnt have any or the target is not actual - if (!target || me->GetVictim() != target) + if (!_fireBlastTimer) { - Unit* ownerTarget = nullptr; - if (Player* playerOwner = me->GetCharmerOrOwner()->ToPlayer()) - ownerTarget = playerOwner->GetSelectedUnit(); - - // recognize which victim will be choosen - if (ownerTarget && ownerTarget->GetTypeId() == TYPEID_PLAYER) - { - if (!ownerTarget->HasBreakableByDamageCrowdControlAura(ownerTarget)) - me->Attack(ownerTarget, false); - } - else if (ownerTarget && (ownerTarget->GetTypeId() != TYPEID_PLAYER) && IsInThreatList(ownerTarget)) - { - if (!ownerTarget->HasBreakableByDamageCrowdControlAura(ownerTarget)) - me->Attack(ownerTarget, false); - } - else - Init(); + DoCastVictim(SPELL_MAGE_FIRE_BLAST); + _fireBlastTimer = TIMER_MIRROR_IMAGE_FIRE_BLAST; } + else + DoCastVictim(SPELL_MAGE_FROST_BOLT); + } - if (uint32 spellId = events.ExecuteEvent()) - { - if (spellId == SPELL_MAGE_FROST_BOLT) - { - events.ScheduleEvent(SPELL_MAGE_FROST_BOLT, TIMER_MIRROR_IMAGE_FROST_BOLT); - DoCastVictim(spellId); - } - else if (spellId == SPELL_MAGE_FIRE_BLAST) - { - DoCastVictim(spellId); - events.ScheduleEvent(SPELL_MAGE_FIRE_BLAST, TIMER_MIRROR_IMAGE_FIRE_BLAST); - } - } + bool CanAIAttack(Unit const* who) const override + { + Unit* owner = me->GetOwner(); + return owner && who->IsAlive() && + !who->HasBreakableByDamageCrowdControlAura() && + who->IsInCombatWith(owner) && ScriptedAI::CanAIAttack(who); } // Do not reload Creature templates on evade mode enter - prevent visual lost @@ -214,8 +193,9 @@ class npc_pet_mage_mirror_image : public CreatureScript me->GetMotionMaster()->Clear(false); me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE); } - Init(); } + + uint32 _fireBlastTimer = 0; }; CreatureAI* GetAI(Creature* creature) const override |