aboutsummaryrefslogtreecommitdiff
path: root/src/server/scripts/Pet
diff options
context:
space:
mode:
authorccrs <ccrs@users.noreply.github.com>2021-08-20 03:41:49 +0200
committerGitHub <noreply@github.com>2021-08-20 03:41:49 +0200
commit3e886c55611357581ecb69c671d021d29b715e64 (patch)
tree6ce73038b0778656f44617bc7a4cd0a529a151f4 /src/server/scripts/Pet
parent1ddd9dc19cc1df1a1ab8c6123283999f9dea6760 (diff)
Scripts/Pet: implement Dance Rune Weapon + minor tweaks to npc_pet_mage_mirror_image script
damage is now halved, years ago it wasn't, so we got that going at least stat scaling missing - the low damage might even come from this instead of the aura periodic auras still unknown
Diffstat (limited to 'src/server/scripts/Pet')
-rw-r--r--src/server/scripts/Pet/pet_dk.cpp227
-rw-r--r--src/server/scripts/Pet/pet_mage.cpp137
2 files changed, 284 insertions, 80 deletions
diff --git a/src/server/scripts/Pet/pet_dk.cpp b/src/server/scripts/Pet/pet_dk.cpp
index 33d08e8be1c..0e26ff8618b 100644
--- a/src/server/scripts/Pet/pet_dk.cpp
+++ b/src/server/scripts/Pet/pet_dk.cpp
@@ -25,15 +25,27 @@
#include "CellImpl.h"
#include "GridNotifiersImpl.h"
#include "MotionMaster.h"
+#include "ObjectAccessor.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
enum DeathKnightSpells
{
- SPELL_DK_SUMMON_GARGOYLE_1 = 49206,
- SPELL_DK_SUMMON_GARGOYLE_2 = 50514,
- SPELL_DK_DISMISS_GARGOYLE = 50515,
- SPELL_DK_SANCTUARY = 54661
+ SPELL_DK_SUMMON_GARGOYLE_1 = 49206,
+ SPELL_DK_SUMMON_GARGOYLE_2 = 50514,
+ SPELL_DK_DISMISS_GARGOYLE = 50515,
+ SPELL_DK_SANCTUARY = 54661,
+ SPELL_DK_DANCING_RUNE_WEAPON = 49028,
+ SPELL_COPY_WEAPON = 63416,
+ SPELL_DK_RUNE_WEAPON_MARK = 50474,
+ SPELL_DK_DANCING_RUNE_WEAPON_VISUAL = 53160,
+ SPELL_FAKE_AGGRO_RADIUS_8_YARD = 49812,
+ SPELL_DK_RUNE_WEAPON_SCALING_01 = 51905,
+ SPELL_DK_RUNE_WEAPON_SCALING = 51906,
+ SPELL_PET_SCALING__MASTER_SPELL_06__SPELL_HIT_EXPERTISE_SPELL_PENETRATION = 67561,
+ SPELL_DK_PET_SCALING_03 = 61697,
+ SPELL_AGGRO_8_YD_PBAE = 49813,
+ SPELL_DISMISS_RUNEBLADE = 50707, // Right now despawn is done by its duration
};
struct npc_pet_dk_ebon_gargoyle : CasterAI
@@ -117,32 +129,215 @@ struct npc_pet_dk_guardian : public AggressorAI
}
};
-// 51963 - Gargoyle Strike
-class spell_pet_dk_gargoyle_strike : public SpellScript
+enum DancingRuneWeaponMisc
{
- PrepareSpellScript(spell_pet_dk_gargoyle_strike);
+ TASK_GROUP_COMBAT = 1,
+ DATA_INITIAL_TARGET_GUID = 1,
+};
+
+struct npc_pet_dk_rune_weapon : ScriptedAI
+{
+ npc_pet_dk_rune_weapon(Creature* creature) : ScriptedAI(creature) { }
+
+ void IsSummonedBy(WorldObject* summoner) override
+ {
+ me->SetReactState(REACT_PASSIVE);
+
+ if (summoner->GetTypeId() != TYPEID_UNIT)
+ return;
+
+ Unit* unitSummoner = summoner->ToUnit();
+
+ DoCast(unitSummoner, SPELL_COPY_WEAPON, true);
+ DoCast(unitSummoner, SPELL_DK_RUNE_WEAPON_MARK, true);
+ DoCastSelf(SPELL_DK_DANCING_RUNE_WEAPON_VISUAL, true);
+ DoCastSelf(SPELL_FAKE_AGGRO_RADIUS_8_YARD, true);
+ DoCastSelf(SPELL_DK_RUNE_WEAPON_SCALING_01, true);
+ DoCastSelf(SPELL_DK_RUNE_WEAPON_SCALING, true);
+ DoCastSelf(SPELL_PET_SCALING__MASTER_SPELL_06__SPELL_HIT_EXPERTISE_SPELL_PENETRATION, true);
+ DoCastSelf(SPELL_DK_PET_SCALING_03, true);
+
+ _scheduler.Schedule(500ms, [this](TaskContext /*activate*/)
+ {
+ me->SetReactState(REACT_AGGRESSIVE);
+ if (!_targetGUID.IsEmpty())
+ {
+ if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID))
+ me->EngageWithTarget(target);
+ }
+ }).Schedule(6s, [this](TaskContext visual)
+ {
+ // Cast every 6 seconds
+ DoCastSelf(SPELL_DK_DANCING_RUNE_WEAPON_VISUAL, true);
+ visual.Repeat();
+ });
+ }
+
+ void SetGUID(ObjectGuid const& guid, int32 id) override
+ {
+ if (id == DATA_INITIAL_TARGET_GUID)
+ _targetGUID = guid;
+ }
+
+ void JustEnteredCombat(Unit* who) override
+ {
+ ScriptedAI::JustEnteredCombat(who);
+
+ // Investigate further if these casts are done by any owned aura, eitherway SMSG_SPELL_GO is sent every X seconds.
+ _scheduler.Schedule(1s, TASK_GROUP_COMBAT, [this](TaskContext aggro8YD)
+ {
+ // Cast every second
+ if (Unit* victim = me->GetVictim())
+ DoCast(victim, SPELL_AGGRO_8_YD_PBAE, true);
+ aggro8YD.Repeat();
+ });
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ Unit* owner = me->GetOwner();
+ if (!owner)
+ {
+ me->DespawnOrUnsummon();
+ return;
+ }
+
+ _scheduler.Update(diff);
+
+ if (!UpdateRuneWeaponVictim())
+ return;
+
+ DoMeleeAttackIfReady();
+ }
+
+ bool CanAIAttack(Unit const* who) const override
+ {
+ Unit* owner = me->GetOwner();
+ return owner && who->IsAlive() && me->IsValidAttackTarget(who) && !who->HasBreakableByDamageCrowdControlAura() && who->IsInCombatWith(owner) && ScriptedAI::CanAIAttack(who);
+ }
- void HandleDamageCalc(SpellEffIndex /*effIndex*/)
+ // Do not reload Creature templates on evade mode enter - prevent visual lost
+ void EnterEvadeMode(EvadeReason /*why*/) override
{
- int32 damage = 60;
- if (Unit* caster = GetCaster())
+ _scheduler.CancelGroup(TASK_GROUP_COMBAT);
+
+ if (!me->IsAlive())
{
- if (caster->GetLevel() >= 60)
- damage += (caster->GetLevel() - 60) * 4;
+ EngagementOver();
+ return;
}
- SetEffectValue(damage);
+ Unit* owner = me->GetCharmerOrOwner();
+
+ me->CombatStop(true);
+ me->SetLootRecipient(nullptr);
+ me->ResetPlayerDamageReq();
+ me->SetLastDamagedTime(0);
+ me->SetCannotReachTarget(false);
+ me->DoNotReacquireSpellFocusTarget();
+ me->SetTarget(ObjectGuid::Empty);
+ EngagementOver();
+
+ if (owner && !me->HasUnitState(UNIT_STATE_FOLLOW))
+ {
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle());
+ }
}
- void Register() override
+private:
+ // 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 UpdateRuneWeaponVictim()
{
- OnEffectLaunchTarget += SpellEffectFn(spell_pet_dk_gargoyle_strike::HandleDamageCalc, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
+ Unit* owner = me->GetOwner();
+ if (!owner)
+ return false;
+
+ if (!me->IsEngaged() && !owner->IsInCombat())
+ return false;
+
+ Unit* currentTarget = me->GetVictim();
+ if (currentTarget && !CanAIAttack(currentTarget))
+ {
+ me->InterruptNonMeleeSpells(true); // do not finish casting on invalid targets
+ me->AttackStop();
+ currentTarget = nullptr;
+ }
+
+ Unit* selectedTarget = nullptr;
+
+ // first, try to get the initial target
+ if (Unit* initialTarget = ObjectAccessor::GetUnit(*me, _targetGUID))
+ {
+ if (CanAIAttack(initialTarget))
+ selectedTarget = initialTarget;
+ }
+ else if (!_targetGUID.IsEmpty())
+ _targetGUID.Clear();
+
+ CombatManager const& mgr = owner->GetCombatManager();
+ if (!selectedTarget)
+ {
+ if (mgr.HasPvPCombat())
+ {
+ // select pvp target
+ float minDistance = 0.f;
+ for (auto const& pair : mgr.GetPvPCombatRefs())
+ {
+ Unit* target = pair.second->GetOther(owner);
+ if (target->GetTypeId() != TYPEID_PLAYER)
+ continue;
+ if (!CanAIAttack(target))
+ continue;
+
+ float dist = owner->GetDistance(target);
+ if (!selectedTarget || dist < minDistance)
+ {
+ selectedTarget = target;
+ minDistance = dist;
+ }
+ }
+ }
+ }
+
+ if (!selectedTarget)
+ {
+ // select pve target
+ float maxThreat = 0.f;
+ for (auto const& pair : mgr.GetPvECombatRefs())
+ {
+ Unit* target = pair.second->GetOther(owner);
+ if (!CanAIAttack(target))
+ continue;
+
+ float threat = target->GetThreatManager().GetThreat(owner);
+ if (threat >= maxThreat)
+ {
+ selectedTarget = target;
+ maxThreat = threat;
+ }
+ }
+ }
+
+ if (!selectedTarget)
+ {
+ EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
+ return false;
+ }
+
+ if (selectedTarget != me->GetVictim())
+ AttackStart(selectedTarget);
+ return true;
}
+
+ TaskScheduler _scheduler;
+ ObjectGuid _targetGUID;
};
void AddSC_deathknight_pet_scripts()
{
RegisterCreatureAI(npc_pet_dk_ebon_gargoyle);
RegisterCreatureAI(npc_pet_dk_guardian);
- RegisterSpellScript(spell_pet_dk_gargoyle_strike);
+ RegisterCreatureAI(npc_pet_dk_rune_weapon);
}
diff --git a/src/server/scripts/Pet/pet_mage.cpp b/src/server/scripts/Pet/pet_mage.cpp
index 06797c4a155..b2d479e4845 100644
--- a/src/server/scripts/Pet/pet_mage.cpp
+++ b/src/server/scripts/Pet/pet_mage.cpp
@@ -28,6 +28,8 @@
#include "Pet.h"
#include "PetAI.h"
#include "ScriptedCreature.h"
+#include "SpellHistory.h"
+#include "Timer.h"
enum MageSpells
{
@@ -46,7 +48,7 @@ struct npc_pet_mage_mirror_image : ScriptedAI
{
const float CHASE_DISTANCE = 35.0f;
- npc_pet_mage_mirror_image(Creature* creature) : ScriptedAI(creature) { }
+ npc_pet_mage_mirror_image(Creature* creature) : ScriptedAI(creature), _fireBlastTimer(0) { }
void InitializeAI() override
{
@@ -60,15 +62,76 @@ struct npc_pet_mage_mirror_image : ScriptedAI
owner->CastSpell(me, SPELL_MAGE_CLONE_ME, true);
}
+ void UpdateAI(uint32 diff) override
+ {
+ Unit* owner = me->GetOwner();
+ if (!owner)
+ {
+ me->DespawnOrUnsummon();
+ return;
+ }
+
+ if (!_fireBlastTimer.Passed())
+ _fireBlastTimer.Update(diff);
+
+ if (!UpdateImageVictim())
+ return;
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ if (_fireBlastTimer.Passed())
+ {
+ DoCastVictim(SPELL_MAGE_FIRE_BLAST);
+ _fireBlastTimer.Reset(TIMER_MIRROR_IMAGE_FIRE_BLAST);
+ }
+ else
+ DoCastVictim(SPELL_MAGE_FROST_BOLT);
+ }
+
+ bool CanAIAttack(Unit const* who) const override
+ {
+ Unit* owner = me->GetOwner();
+ return owner && who->IsAlive() && me->IsValidAttackTarget(who) && !who->HasBreakableByDamageCrowdControlAura() && who->IsInCombatWith(owner) && ScriptedAI::CanAIAttack(who);
+ }
+
+ // Do not reload Creature templates on evade mode enter - prevent visual lost
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ if (!me->IsAlive())
+ {
+ EngagementOver();
+ return;
+ }
+
+ Unit* owner = me->GetCharmerOrOwner();
+
+ me->CombatStop(true);
+ me->SetLootRecipient(nullptr);
+ me->ResetPlayerDamageReq();
+ me->SetLastDamagedTime(0);
+ me->SetCannotReachTarget(false);
+ me->DoNotReacquireSpellFocusTarget();
+ me->SetTarget(ObjectGuid::Empty);
+ EngagementOver();
+
+ if (owner && !me->HasUnitState(UNIT_STATE_FOLLOW))
+ {
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle());
+ }
+ }
+
+private:
// 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()
+ bool UpdateImageVictim()
{
Unit* owner = me->GetOwner();
if (!owner)
return false;
- if (!me->HasUnitState(UNIT_STATE_CASTING) && !me->IsInCombat() && !owner->IsInCombat())
+ if (!me->HasUnitState(UNIT_STATE_CASTING) && !me->IsEngaged() && !owner->IsInCombat())
return false;
Unit* currentTarget = me->GetVictim();
@@ -86,8 +149,9 @@ struct npc_pet_mage_mirror_image : ScriptedAI
Unit* selectedTarget = nullptr;
CombatManager const& mgr = owner->GetCombatManager();
if (mgr.HasPvPCombat())
- { // select pvp target
- float minDistance = 0.0f;
+ {
+ // select pvp target
+ float minDistance = 0.f;
for (auto const& pair : mgr.GetPvPCombatRefs())
{
Unit* target = pair.second->GetOther(owner);
@@ -106,8 +170,9 @@ struct npc_pet_mage_mirror_image : ScriptedAI
}
if (!selectedTarget)
- { // select pve target
- float maxThreat = 0.0f;
+ {
+ // select pve target
+ float maxThreat = 0.f;
for (auto const& pair : mgr.GetPvECombatRefs())
{
Unit* target = pair.second->GetOther(owner);
@@ -134,63 +199,7 @@ struct npc_pet_mage_mirror_image : ScriptedAI
return true;
}
- void UpdateAI(uint32 diff) override
- {
- Unit* owner = me->GetOwner();
- if (!owner)
- {
- me->DespawnOrUnsummon();
- return;
- }
-
- if (_fireBlastTimer)
- {
- if (_fireBlastTimer <= diff)
- _fireBlastTimer = 0;
- else
- _fireBlastTimer -= diff;
- }
-
- if (!UpdateVictim())
- return;
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
-
- if (!_fireBlastTimer)
- {
- DoCastVictim(SPELL_MAGE_FIRE_BLAST);
- _fireBlastTimer = TIMER_MIRROR_IMAGE_FIRE_BLAST;
- }
- else
- DoCastVictim(SPELL_MAGE_FROST_BOLT);
- }
-
- bool CanAIAttack(Unit const* who) const override
- {
- Unit* owner = me->GetOwner();
- return owner && who->IsAlive() && me->IsValidAttackTarget(who) &&
- !who->HasBreakableByDamageCrowdControlAura() &&
- who->IsInCombatWith(owner) && ScriptedAI::CanAIAttack(who);
- }
-
- // Do not reload Creature templates on evade mode enter - prevent visual lost
- void EnterEvadeMode(EvadeReason /*why*/) override
- {
- if (me->IsInEvadeMode() || !me->IsAlive())
- return;
-
- Unit* owner = me->GetCharmerOrOwner();
-
- me->CombatStop(true);
- if (owner && !me->HasUnitState(UNIT_STATE_FOLLOW))
- {
- me->GetMotionMaster()->Clear();
- me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle());
- }
- }
-
- uint32 _fireBlastTimer = 0;
+ TimeTracker _fireBlastTimer;
};
void AddSC_mage_pet_scripts()