aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/scripts/Pet/pet_mage.cpp216
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