diff options
| author | ccrs <ccrs@users.noreply.github.com> | 2021-08-20 03:41:49 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-20 03:41:49 +0200 |
| commit | 3e886c55611357581ecb69c671d021d29b715e64 (patch) | |
| tree | 6ce73038b0778656f44617bc7a4cd0a529a151f4 /src/server/scripts/Pet | |
| parent | 1ddd9dc19cc1df1a1ab8c6123283999f9dea6760 (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.cpp | 227 | ||||
| -rw-r--r-- | src/server/scripts/Pet/pet_mage.cpp | 137 |
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() |
