From 08035b4b84609c62c227b2c6a617b67c9f43cadc Mon Sep 17 00:00:00 2001 From: Dinkledork <118951051+Day36512@users.noreply.github.com> Date: Tue, 21 Mar 2023 10:56:51 -0600 Subject: [PATCH] Remove requirement to be in combat for disengage --- spell_hunter.cpp | 1357 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1357 insertions(+) create mode 100644 spell_hunter.cpp diff --git a/spell_hunter.cpp b/spell_hunter.cpp new file mode 100644 index 0000000..464a625 --- /dev/null +++ b/spell_hunter.cpp @@ -0,0 +1,1357 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/* + * Scripts for spells with SPELLFAMILY_HUNTER, SPELLFAMILY_PET and SPELLFAMILY_GENERIC spells used by hunter players. + * Ordered alphabetically using scriptname. + * Scriptnames of files in this file should be prefixed with "spell_hun_". + */ + +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "Pet.h" +#include "ScriptMgr.h" +#include "SpellAuraEffects.h" +#include "SpellAuras.h" +#include "SpellMgr.h" +#include "SpellScript.h" + +/// @todo: this import is not necessary for compilation and marked as unused by the IDE +// however, for some reasons removing it would cause a damn linking issue +// there is probably some underlying problem with imports which should properly addressed +// see: https://github.com/azerothcore/azerothcore-wotlk/issues/9766 +#include "GridNotifiersImpl.h" + +enum HunterSpells +{ + // Ours + SPELL_HUNTER_WYVERN_STING_DOT = 24131, + + // Theirs + SPELL_HUNTER_ASPECT_OF_THE_BEAST = 13161, + SPELL_HUNTER_ASPECT_OF_THE_BEAST_PET = 61669, + SPELL_HUNTER_ASPECT_OF_THE_VIPER = 34074, + SPELL_HUNTER_ASPECT_OF_THE_VIPER_ENERGIZE = 34075, + SPELL_HUNTER_BESTIAL_WRATH = 19574, + SPELL_HUNTER_CHIMERA_SHOT_SERPENT = 53353, + SPELL_HUNTER_CHIMERA_SHOT_VIPER = 53358, + SPELL_HUNTER_CHIMERA_SHOT_SCORPID = 53359, + SPELL_HUNTER_GLYPH_OF_ASPECT_OF_THE_VIPER = 56851, + SPELL_HUNTER_IMPROVED_MEND_PET = 24406, + SPELL_HUNTER_INVIGORATION_TRIGGERED = 53398, + SPELL_HUNTER_MASTERS_CALL_TRIGGERED = 62305, + SPELL_HUNTER_MISDIRECTION_PROC = 35079, + SPELL_HUNTER_PET_LAST_STAND_TRIGGERED = 53479, + SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX = 55709, + SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_TRIGGERED = 54114, + SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_DEBUFF = 55711, + SPELL_HUNTER_PET_CARRION_FEEDER_TRIGGERED = 54045, + SPELL_HUNTER_READINESS = 23989, + SPELL_HUNTER_SNIPER_TRAINING_R1 = 53302, + SPELL_HUNTER_SNIPER_TRAINING_BUFF_R1 = 64418, + SPELL_HUNTER_VICIOUS_VIPER = 61609, + SPELL_HUNTER_VIPER_ATTACK_SPEED = 60144, + SPELL_DRAENEI_GIFT_OF_THE_NAARU = 59543, + SPELL_HUNTER_GLYPH_OF_ARCANE_SHOT = 61389, + SPELL_LOCK_AND_LOAD_TRIGGER = 56453, + SPELL_LOCK_AND_LOAD_MARKER = 67544 +}; + +class spell_hun_check_pet_los : public SpellScript +{ + PrepareSpellScript(spell_hun_check_pet_los); + + SpellCastResult CheckCast() + { + Unit* pet = GetCaster()->GetGuardianPet(); + if (!pet) + pet = GetCaster()->GetCharm(); + + if (!pet) + return SPELL_FAILED_NO_PET; + + if (!pet->IsAlive()) + { + SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_PET_IS_DEAD); + return SPELL_FAILED_CUSTOM_ERROR; + } + + if (!GetCaster()->IsWithinLOSInMap(pet)) + return SPELL_FAILED_LINE_OF_SIGHT; + + return SPELL_CAST_OK; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_hun_check_pet_los::CheckCast); + } +}; + +class spell_hun_cower : public AuraScript +{ + PrepareAuraScript(spell_hun_cower); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + if (AuraEffect* aurEff = GetUnitOwner()->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_PET, GetSpellInfo()->SpellIconID, EFFECT_0)) + AddPct(amount, aurEff->GetAmount()); + } + + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_hun_cower::CalculateAmount, EFFECT_1, SPELL_AURA_MOD_DECREASE_SPEED); + } +}; + +class spell_hun_wyvern_sting : public AuraScript +{ + PrepareAuraScript(spell_hun_wyvern_sting) + + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + caster->CastSpell(GetTarget(), sSpellMgr->GetSpellWithRank(SPELL_HUNTER_WYVERN_STING_DOT, GetSpellInfo()->GetRank()), true); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_hun_wyvern_sting::HandleEffectRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + } +}; + +class spell_hun_animal_handler : public AuraScript +{ + PrepareAuraScript(spell_hun_animal_handler); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + amount = 0; + if (Unit* owner = GetUnitOwner()->GetOwner()) + if (AuraEffect const* animalHandlerEff = owner->GetDummyAuraEffect(SPELLFAMILY_HUNTER, 2234, EFFECT_1)) + amount = animalHandlerEff->GetAmount(); + } + + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_hun_animal_handler::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_ATTACK_POWER_PCT); + } +}; + +class spell_hun_generic_scaling : public AuraScript +{ + PrepareAuraScript(spell_hun_generic_scaling); + + void CalculateResistanceAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) + { + // xinef: pet inherits 40% of resistance from owner and 35% of armor + if (Unit* owner = GetUnitOwner()->GetOwner()) + { + SpellSchoolMask schoolMask = SpellSchoolMask(aurEff->GetSpellInfo()->Effects[aurEff->GetEffIndex()].MiscValue); + int32 modifier = schoolMask == SPELL_SCHOOL_MASK_NORMAL ? 35 : 40; + amount = CalculatePct(std::max(0, owner->GetResistance(schoolMask)), modifier); + } + } + + void CalculateStatAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) + { + if (Unit* owner = GetUnitOwner()->GetOwner()) + { + // xinef: by default pet inherits 45% of stamina + int32 modifier = 45; + + // xinef: Wild Hunt bonus for stamina + if (AuraEffect* wildHuntEff = GetUnitOwner()->GetDummyAuraEffect(SPELLFAMILY_PET, 3748, EFFECT_0)) + AddPct(modifier, wildHuntEff->GetAmount()); + + amount = CalculatePct(std::max(0, owner->GetStat(Stats(aurEff->GetSpellInfo()->Effects[aurEff->GetEffIndex()].MiscValue))), modifier); + } + } + + void CalculateAPAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + if (Unit* owner = GetUnitOwner()->GetOwner()) + { + // xinef: by default 22% of RAP + int32 modifier = 22; + + // xinef: Wild Hunt bonus for AP + if (AuraEffect* wildHuntEff = GetUnitOwner()->GetDummyAuraEffect(SPELLFAMILY_PET, 3748, EFFECT_1)) + AddPct(modifier, wildHuntEff->GetAmount()); + + float ownerAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK); + + // Xinef: Hunter vs. Wild + if (AuraEffect* HvWEff = owner->GetAuraEffect(SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT, SPELLFAMILY_HUNTER, 3647, EFFECT_0)) + ownerAP += CalculatePct(owner->GetStat(STAT_STAMINA), HvWEff->GetAmount()); + + amount = CalculatePct(std::max(0, ownerAP), modifier); + } + } + + void CalculateSPAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + if (Unit* owner = GetUnitOwner()->GetOwner()) + { + // xinef: by default 12.87% of RAP + float modifier = 12.87f; + + // xinef: Wild Hunt bonus for AP + if (AuraEffect* wildHuntEff = GetUnitOwner()->GetDummyAuraEffect(SPELLFAMILY_PET, 3748, EFFECT_1)) + AddPct(modifier, wildHuntEff->GetAmount()); + + amount = CalculatePct(std::max(0, owner->GetTotalAttackPowerValue(RANGED_ATTACK)), modifier); + + // xinef: Update appropriate player field + if (owner->GetTypeId() == TYPEID_PLAYER) + owner->SetUInt32Value(PLAYER_PET_SPELL_POWER, (uint32)amount); + } + } + + void CalcPeriodic(AuraEffect const* /*aurEff*/, bool& isPeriodic, int32& amplitude) + { + isPeriodic = true; + amplitude = 2 * IN_MILLISECONDS; + } + + void HandlePeriodic(AuraEffect const* aurEff) + { + PreventDefaultAction(); + if (aurEff->GetAuraType() == SPELL_AURA_MOD_STAT && (aurEff->GetMiscValue() == STAT_STAMINA || aurEff->GetMiscValue() == STAT_INTELLECT)) + { + int32 currentAmount = aurEff->GetAmount(); + int32 newAmount = GetEffect(aurEff->GetEffIndex())->CalculateAmount(GetCaster()); + if (newAmount != currentAmount) + { + if (aurEff->GetMiscValue() == STAT_STAMINA) + { + uint32 actStat = GetUnitOwner()->GetHealth(); + GetEffect(aurEff->GetEffIndex())->ChangeAmount(newAmount, false); + GetUnitOwner()->SetHealth(std::min(GetUnitOwner()->GetMaxHealth(), actStat)); + } + else + { + uint32 actStat = GetUnitOwner()->GetPower(POWER_MANA); + GetEffect(aurEff->GetEffIndex())->ChangeAmount(newAmount, false); + GetUnitOwner()->SetPower(POWER_MANA, std::min(GetUnitOwner()->GetMaxPower(POWER_MANA), actStat)); + } + } + } + else + GetEffect(aurEff->GetEffIndex())->RecalculateAmount(); + } + + void Register() override + { + if (m_scriptSpellId != 34902) + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_hun_generic_scaling::CalculateResistanceAmount, EFFECT_ALL, SPELL_AURA_MOD_RESISTANCE); + else + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_hun_generic_scaling::CalculateStatAmount, EFFECT_ALL, SPELL_AURA_MOD_STAT); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_hun_generic_scaling::CalculateAPAmount, EFFECT_ALL, SPELL_AURA_MOD_ATTACK_POWER); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_hun_generic_scaling::CalculateSPAmount, EFFECT_ALL, SPELL_AURA_MOD_DAMAGE_DONE); + } + + DoEffectCalcPeriodic += AuraEffectCalcPeriodicFn(spell_hun_generic_scaling::CalcPeriodic, EFFECT_ALL, SPELL_AURA_ANY); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_hun_generic_scaling::HandlePeriodic, EFFECT_ALL, SPELL_AURA_ANY); + } +}; + +// Taming the Beast quests (despawn creature after dismiss) +class spell_hun_taming_the_beast : public AuraScript +{ + PrepareAuraScript(spell_hun_taming_the_beast); + + void HandleOnEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* target = GetTarget()) + { + if (Creature* creature = target->ToCreature()) + { + creature->GetThreatMgr().ClearAllThreat(); + } + } + } + + void HandleOnEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* target = GetTarget()) + if (Creature* creature = target->ToCreature()) + creature->DespawnOrUnsummon(1); + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_hun_taming_the_beast::HandleOnEffectApply, EFFECT_0, SPELL_AURA_MOD_CHARM, AURA_EFFECT_HANDLE_REAL); + OnEffectRemove += AuraEffectRemoveFn(spell_hun_taming_the_beast::HandleOnEffectRemove, EFFECT_0, SPELL_AURA_MOD_CHARM, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 13161 Aspect of the Beast +class spell_hun_aspect_of_the_beast : public AuraScript +{ + PrepareAuraScript(spell_hun_aspect_of_the_beast); + + bool Load() override + { + return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_ASPECT_OF_THE_BEAST_PET }); + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetCaster()) + if (Player* caster = GetCaster()->ToPlayer()) + if (Pet* pet = caster->GetPet()) + pet->RemoveAurasDueToSpell(SPELL_HUNTER_ASPECT_OF_THE_BEAST_PET); + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetCaster()) + if (Player* caster = GetCaster()->ToPlayer()) + if (caster->GetPet()) + caster->CastSpell(caster, SPELL_HUNTER_ASPECT_OF_THE_BEAST_PET, true); + } + + void OnPetApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* pet = GetUnitOwner(); + if (Unit* owner = pet->GetOwner()) + if (owner->HasAura(SPELL_HUNTER_ASPECT_OF_THE_BEAST)) + return; + + SetDuration(0); + } + + void Register() override + { + if (m_scriptSpellId == 13161) + { + AfterEffectApply += AuraEffectApplyFn(spell_hun_aspect_of_the_beast::OnApply, EFFECT_0, SPELL_AURA_UNTRACKABLE, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_hun_aspect_of_the_beast::OnRemove, EFFECT_0, SPELL_AURA_UNTRACKABLE, AURA_EFFECT_HANDLE_REAL); + } + else + AfterEffectApply += AuraEffectApplyFn(spell_hun_aspect_of_the_beast::OnPetApply, EFFECT_0, SPELL_AURA_UNTRACKABLE, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 34074 - Aspect of the Viper +class spell_hun_ascpect_of_the_viper : public AuraScript +{ + PrepareAuraScript(spell_hun_ascpect_of_the_viper); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_HUNTER_ASPECT_OF_THE_VIPER_ENERGIZE, + SPELL_HUNTER_GLYPH_OF_ASPECT_OF_THE_VIPER, + SPELL_HUNTER_VIPER_ATTACK_SPEED, + SPELL_HUNTER_VICIOUS_VIPER + }); + } + + bool CheckProc(ProcEventInfo& procInfo) + { + SpellInfo const* spellInfo = procInfo.GetSpellInfo(); + // Xinef: cannot proc from volley damage + if (spellInfo && (spellInfo->SpellFamilyFlags[0] & 0x2000) && spellInfo->Effects[EFFECT_0].Effect == SPELL_EFFECT_SCHOOL_DAMAGE) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + + uint32 maxMana = GetTarget()->GetMaxPower(POWER_MANA); + int32 mana = CalculatePct(maxMana, GetTarget()->GetAttackTime(RANGED_ATTACK) / 1000.0f); + + if (AuraEffect const* glyph = GetTarget()->GetAuraEffect(SPELL_HUNTER_GLYPH_OF_ASPECT_OF_THE_VIPER, EFFECT_0)) + AddPct(mana, glyph->GetAmount()); + + GetTarget()->CastCustomSpell(SPELL_HUNTER_ASPECT_OF_THE_VIPER_ENERGIZE, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, nullptr, aurEff); + } + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + // Hunter T7 4P Bonus + if (GetTarget()->HasAura(SPELL_HUNTER_VIPER_ATTACK_SPEED)) + GetTarget()->CastSpell(GetTarget(), SPELL_HUNTER_VICIOUS_VIPER, true, nullptr, aurEff); + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + // Hunter T7 4P Bonus + if (GetTarget()->HasAura(SPELL_HUNTER_VIPER_ATTACK_SPEED)) + GetTarget()->RemoveAurasDueToSpell(SPELL_HUNTER_VICIOUS_VIPER); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_hun_ascpect_of_the_viper::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_hun_ascpect_of_the_viper::HandleProc, EFFECT_2, SPELL_AURA_DUMMY); + AfterEffectApply += AuraEffectApplyFn(spell_hun_ascpect_of_the_viper::OnApply, EFFECT_0, SPELL_AURA_OBS_MOD_POWER, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_hun_ascpect_of_the_viper::OnRemove, EFFECT_0, SPELL_AURA_OBS_MOD_POWER, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 53209 Chimera Shot +class spell_hun_chimera_shot : public SpellScript +{ + PrepareSpellScript(spell_hun_chimera_shot); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_CHIMERA_SHOT_SERPENT, SPELL_HUNTER_CHIMERA_SHOT_VIPER, SPELL_HUNTER_CHIMERA_SHOT_SCORPID }); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + if (Unit* unitTarget = GetHitUnit()) + { + uint32 spellId = 0; + int32 basePoint = 0; + Unit::AuraApplicationMap& Auras = unitTarget->GetAppliedAuras(); + for (Unit::AuraApplicationMap::iterator i = Auras.begin(); i != Auras.end(); ++i) + { + Aura* aura = i->second->GetBase(); + if (aura->GetCasterGUID() != caster->GetGUID()) + continue; + + // Search only Serpent Sting, Viper Sting, Scorpid Sting auras + flag96 familyFlag = aura->GetSpellInfo()->SpellFamilyFlags; + if (!(familyFlag[1] & 0x00000080 || familyFlag[0] & 0x0000C000)) + continue; + if (AuraEffect* aurEff = aura->GetEffect(0)) + { + // Serpent Sting - Instantly deals 40% of the damage done by your Serpent Sting. + if (familyFlag[0] & 0x4000) + { + int32 TickCount = aurEff->GetTotalTicks(); + spellId = SPELL_HUNTER_CHIMERA_SHOT_SERPENT; + basePoint = aurEff->GetAmount(); + ApplyPct(basePoint, TickCount * 40); + basePoint = unitTarget->SpellDamageBonusTaken(caster, aura->GetSpellInfo(), basePoint, DOT, aura->GetStackAmount()); + } + // Viper Sting - Instantly restores mana to you equal to 60% of the total amount drained by your Viper Sting. + else if (familyFlag[1] & 0x00000080) + { + int32 TickCount = aura->GetEffect(0)->GetTotalTicks(); + spellId = SPELL_HUNTER_CHIMERA_SHOT_VIPER; + + // Amount of one aura tick + basePoint = int32(CalculatePct(unitTarget->GetMaxPower(POWER_MANA), aurEff->GetAmount())); + int32 casterBasePoint = aurEff->GetAmount() * unitTarget->GetMaxPower(POWER_MANA) / 50; /// @todo: Caster uses unitTarget? + if (basePoint > casterBasePoint) + basePoint = casterBasePoint; + ApplyPct(basePoint, TickCount * 60); + } + // Scorpid Sting - Attempts to Disarm the target for 10 sec. This effect cannot occur more than once per 1 minute. + else if (familyFlag[0] & 0x00008000) + { + if (caster->ToPlayer()) // Scorpid Sting - Add 1 minute cooldown + { + if (caster->ToPlayer()->HasSpellCooldown(SPELL_HUNTER_CHIMERA_SHOT_SCORPID)) + break; + + caster->ToPlayer()->AddSpellCooldown(SPELL_HUNTER_CHIMERA_SHOT_SCORPID, 0, 60000); + } + + spellId = SPELL_HUNTER_CHIMERA_SHOT_SCORPID; + } + + // Refresh aura duration + aura->RefreshDuration(); + aurEff->ChangeAmount(aurEff->CalculateAmount(caster), false); + } + break; + } + + if (spellId) + caster->CastCustomSpell(unitTarget, spellId, &basePoint, 0, 0, true); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_hun_chimera_shot::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// -19572 - Improved Mend Pet +class spell_hun_improved_mend_pet : public AuraScript +{ + PrepareAuraScript(spell_hun_improved_mend_pet); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_IMPROVED_MEND_PET }); + } + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + return roll_chance_i(GetEffect(EFFECT_0)->GetAmount()); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_HUNTER_IMPROVED_MEND_PET, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_hun_improved_mend_pet::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_hun_improved_mend_pet::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } +}; + +// 53412 - Invigoration +class spell_hun_invigoration : public SpellScript +{ + PrepareSpellScript(spell_hun_invigoration); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_INVIGORATION_TRIGGERED }); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* unitTarget = GetHitUnit()) + if (AuraEffect* aurEff = unitTarget->GetDummyAuraEffect(SPELLFAMILY_HUNTER, 3487, 0)) + if (roll_chance_i(aurEff->GetAmount())) + unitTarget->CastSpell(unitTarget, SPELL_HUNTER_INVIGORATION_TRIGGERED, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_hun_invigoration::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 53478 - Last Stand Pet +class spell_hun_last_stand_pet : public SpellScript +{ + PrepareSpellScript(spell_hun_last_stand_pet); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_PET_LAST_STAND_TRIGGERED }); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + int32 healthModSpellBasePoints0 = int32(caster->CountPctFromMaxHealth(30)); + caster->CastCustomSpell(caster, SPELL_HUNTER_PET_LAST_STAND_TRIGGERED, &healthModSpellBasePoints0, nullptr, nullptr, true, nullptr); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_hun_last_stand_pet::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +// 53271 - Masters Call +class spell_hun_masters_call : public SpellScript +{ + PrepareSpellScript(spell_hun_masters_call); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_MASTERS_CALL_TRIGGERED }); + } + + SpellCastResult DoCheckCast() + { + Pet* pet = GetCaster()->ToPlayer()->GetPet(); + if (!pet || !pet->IsAlive()) + return SPELL_FAILED_NO_PET; + + // Do a mini Spell::CheckCasterAuras on the pet, no other way of doing this + SpellCastResult result = SPELL_CAST_OK; + uint32 const unitflag = pet->GetUnitFlags(); + if (pet->GetCharmerGUID()) + result = SPELL_FAILED_CHARMED; + else if (unitflag & UNIT_FLAG_STUNNED) + result = SPELL_FAILED_STUNNED; + else if (unitflag & UNIT_FLAG_FLEEING) + result = SPELL_FAILED_FLEEING; + else if (unitflag & UNIT_FLAG_CONFUSED) + result = SPELL_FAILED_CONFUSED; + + if (result != SPELL_CAST_OK) + return result; + + Unit* target = GetExplTargetUnit(); + if (!target) + return SPELL_FAILED_BAD_TARGETS; + + if (!pet->IsWithinLOSInMap(target)) + return SPELL_FAILED_LINE_OF_SIGHT; + + return SPELL_CAST_OK; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetCaster()->ToPlayer()->GetPet()->CastSpell(GetHitUnit(), GetEffectValue(), true); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell((Unit*)nullptr, SPELL_HUNTER_MASTERS_CALL_TRIGGERED, true); + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_hun_masters_call::DoCheckCast); + + OnEffectHitTarget += SpellEffectFn(spell_hun_masters_call::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + OnEffectHitTarget += SpellEffectFn(spell_hun_masters_call::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 23989 - Readiness +class spell_hun_readiness : public SpellScript +{ + PrepareSpellScript(spell_hun_readiness); + + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Player* caster = GetCaster()->ToPlayer(); + // immediately finishes the cooldown on your other Hunter abilities except Bestial Wrath + + // force removal of the disarm cooldown + caster->RemoveSpellCooldown(SPELL_HUNTER_CHIMERA_SHOT_SCORPID); + + SpellCooldowns& cooldowns = caster->GetSpellCooldownMap(); + + std::set> spellsToRemove; + std::set categoriesToRemove; + + for (const auto& [spellId, cooldown] : cooldowns) + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (spellInfo + && spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER + && spellInfo->Id != SPELL_HUNTER_READINESS + && spellInfo->Id != SPELL_HUNTER_BESTIAL_WRATH + && spellInfo->Id != SPELL_DRAENEI_GIFT_OF_THE_NAARU) + { + if (spellInfo->RecoveryTime > 0) + spellsToRemove.insert(std::make_pair(spellInfo->Id, cooldown.needSendToClient)); + + if (spellInfo->CategoryRecoveryTime > 0) + categoriesToRemove.insert(spellInfo->GetCategory()); + } + } + + // we can't remove spell cooldowns while iterating. + for (const auto& [spellId, sendToClient] : spellsToRemove) + caster->RemoveSpellCooldown(spellId, sendToClient); + for (const auto& category : categoriesToRemove) + caster->RemoveCategoryCooldown(category); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_hun_readiness::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +// 37506 - Scatter Shot +class spell_hun_scatter_shot : public SpellScript +{ + PrepareSpellScript(spell_hun_scatter_shot); + + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Player* caster = GetCaster()->ToPlayer(); + // break Auto Shot and autohit + caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); + caster->AttackStop(); + caster->SendAttackSwingCancelAttack(); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_hun_scatter_shot::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +// -53302 - Sniper Training +class spell_hun_sniper_training : public AuraScript +{ + PrepareAuraScript(spell_hun_sniper_training); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_SNIPER_TRAINING_R1, SPELL_HUNTER_SNIPER_TRAINING_BUFF_R1 }); + } + + void HandlePeriodic(AuraEffect const* aurEff) + { + PreventDefaultAction(); + if (aurEff->GetAmount() <= 0) + { + if (!GetCaster() || !GetTarget()) + { + return; + } + + Unit* target = GetTarget(); + + uint32 spellId = SPELL_HUNTER_SNIPER_TRAINING_BUFF_R1 + GetId() - SPELL_HUNTER_SNIPER_TRAINING_R1; + if (SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(spellId)) + { + Unit* triggerCaster = triggeredSpellInfo->NeedsToBeTriggeredByCaster(GetSpellInfo()) ? GetCaster() : target; + triggerCaster->CastSpell(target, triggeredSpellInfo, true, 0, aurEff); + } + } + } + + void HandleUpdatePeriodic(AuraEffect* aurEff) + { + //npcbot: handle creatures, remove dead trigger + if (!GetUnitOwner()->IsAlive()) + return; + if (Creature const* bot = GetUnitOwner()->ToCreature()) + { + if (!bot->IsNPCBot()) + return; + + int32 baseAmount = aurEff->GetBaseAmount(); + int32 amount = bot->isMoving() || aurEff->GetAmount() <= 0 ? + bot->CalculateSpellDamage(bot, GetSpellInfo(), aurEff->GetEffIndex(), &baseAmount) : + aurEff->GetAmount() - 1; + aurEff->SetAmount(amount); + return; + } + //end npcbot + if (Player* playerTarget = GetUnitOwner()->ToPlayer()) + { + int32 baseAmount = aurEff->GetBaseAmount(); + int32 amount = playerTarget->isMoving() || aurEff->GetAmount() <= 0 ? + playerTarget->CalculateSpellDamage(playerTarget, GetSpellInfo(), aurEff->GetEffIndex(), &baseAmount) : + aurEff->GetAmount() - 1; + aurEff->SetAmount(amount); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_hun_sniper_training::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_hun_sniper_training::HandleUpdatePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } +}; + +// 55709 - Pet Heart of the Phoenix +class spell_hun_pet_heart_of_the_phoenix : public SpellScript +{ + PrepareSpellScript(spell_hun_pet_heart_of_the_phoenix); + + bool Load() override + { + if (!GetCaster()->IsPet()) + return false; + return true; + } + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_TRIGGERED, SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_DEBUFF }); + } + + SpellCastResult CheckCast() + { + Unit* caster = GetCaster(); + if (caster->IsAlive()) + return SPELL_FAILED_TARGET_NOT_DEAD; + if (caster->HasAura(SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_DEBUFF)) + return SPELL_FAILED_CASTER_AURASTATE; + return SPELL_CAST_OK; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + if (Unit* owner = caster->GetOwner()) + if (!caster->HasAura(SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_DEBUFF)) + { + owner->CastCustomSpell(SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_TRIGGERED, SPELLVALUE_BASE_POINT0, 100, caster, true); + caster->CastSpell(caster, SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_DEBUFF, true); + } + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_hun_pet_heart_of_the_phoenix::CheckCast); + OnEffectHitTarget += SpellEffectFn(spell_hun_pet_heart_of_the_phoenix::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 54044 - Pet Carrion Feeder +class spell_hun_pet_carrion_feeder : public SpellScript +{ + PrepareSpellScript(spell_hun_pet_carrion_feeder); + + bool Load() override + { + if (!GetCaster()->IsPet()) + return false; + return true; + } + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_PET_CARRION_FEEDER_TRIGGERED }); + } + + SpellCastResult CheckIfCorpseNear() + { + Unit* caster = GetCaster(); + float max_range = GetSpellInfo()->GetMaxRange(false); + WorldObject* result = nullptr; + // search for nearby enemy corpse in range + Acore::AnyDeadUnitSpellTargetInRangeCheck check(caster, max_range, GetSpellInfo(), TARGET_CHECK_ENEMY); + Acore::WorldObjectSearcher searcher(caster, result, check); + Cell::VisitWorldObjects(caster, searcher, max_range); + if (!result) + { + Cell::VisitGridObjects(caster, searcher, max_range); + } + if (!result) + { + return SPELL_FAILED_NO_EDIBLE_CORPSES; + } + return SPELL_CAST_OK; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + caster->CastSpell(caster, SPELL_HUNTER_PET_CARRION_FEEDER_TRIGGERED, false); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_hun_pet_carrion_feeder::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + OnCheckCast += SpellCheckCastFn(spell_hun_pet_carrion_feeder::CheckIfCorpseNear); + } +}; + +// 34477 - Misdirection +class spell_hun_misdirection : public AuraScript +{ + PrepareAuraScript(spell_hun_misdirection); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_MISDIRECTION_PROC }); + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT || !GetTarget()->HasAura(SPELL_HUNTER_MISDIRECTION_PROC)) + GetTarget()->ResetRedirectThreat(); + } + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + return GetTarget()->GetRedirectThreatTarget(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_HUNTER_MISDIRECTION_PROC, true, nullptr, aurEff); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_hun_misdirection::OnRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + DoCheckProc += AuraCheckProcFn(spell_hun_misdirection::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_hun_misdirection::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); + } +}; + +// 35079 - Misdirection (Proc) +class spell_hun_misdirection_proc : public AuraScript +{ + PrepareAuraScript(spell_hun_misdirection_proc); + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->ResetRedirectThreat(); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_hun_misdirection_proc::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 781 - Disengage +class spell_hun_disengage : public SpellScript +{ + PrepareSpellScript(spell_hun_disengage); + + SpellCastResult CheckCast() + { + Unit* caster = GetCaster(); + if (caster->GetTypeId() == TYPEID_PLAYER) + return SPELL_CAST_OK; + + return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_hun_disengage::CheckCast); + } +}; + +// 1515 - Tame Beast +class spell_hun_tame_beast : public SpellScript +{ + PrepareSpellScript(spell_hun_tame_beast); + + SpellCastResult CheckCast() + { + Unit* caster = GetCaster(); + if (caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_DONT_REPORT; + + Player* player = GetCaster()->ToPlayer(); + + if (!GetExplTargetUnit()) + { + player->SendTameFailure(PET_TAME_INVALID_CREATURE); + return SPELL_FAILED_DONT_REPORT; + } + + if (Creature* target = GetExplTargetUnit()->ToCreature()) + { + if (target->GetLevel() > player->GetLevel()) + { + player->SendTameFailure(PET_TAME_TOO_HIGHLEVEL); + return SPELL_FAILED_DONT_REPORT; + } + + if (target->GetCreatureTemplate()->IsExotic() && !player->CanTameExoticPets()) + { + player->SendTameFailure(PET_TAME_CANT_CONTROL_EXOTIC); + return SPELL_FAILED_DONT_REPORT; + } + + if (!target->GetCreatureTemplate()->IsTameable(player->CanTameExoticPets())) + { + player->SendTameFailure(PET_TAME_NOT_TAMEABLE); + return SPELL_FAILED_DONT_REPORT; + } + + PetStable const* petStable = player->GetPetStable(); + if (petStable) + { + if (petStable->CurrentPet) + return SPELL_FAILED_ALREADY_HAVE_SUMMON; + + if (petStable->GetUnslottedHunterPet()) + { + caster->SendTameFailure(PET_TAME_TOO_MANY); + return SPELL_FAILED_DONT_REPORT; + } + } + + if (player->GetCharmGUID()) + { + player->SendTameFailure(PET_TAME_ANOTHER_SUMMON_ACTIVE); + return SPELL_FAILED_DONT_REPORT; + } + + if (target->GetOwnerGUID()) + { + player->SendTameFailure(PET_TAME_CREATURE_ALREADY_OWNED); + return SPELL_FAILED_DONT_REPORT; + } + } + else + { + player->SendTameFailure(PET_TAME_INVALID_CREATURE); + return SPELL_FAILED_DONT_REPORT; + } + + return SPELL_CAST_OK; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_hun_tame_beast::CheckCast); + } +}; + +// 60144 - Viper Attack Speed +class spell_hun_viper_attack_speed : public AuraScript +{ + PrepareAuraScript(spell_hun_viper_attack_speed); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_ASPECT_OF_THE_VIPER, SPELL_HUNTER_VICIOUS_VIPER }); + } + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (GetTarget()->HasAura(SPELL_HUNTER_ASPECT_OF_THE_VIPER)) + GetTarget()->CastSpell(GetTarget(), SPELL_HUNTER_VICIOUS_VIPER, true, nullptr, aurEff); + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + // possible exploit + GetTarget()->RemoveAurasDueToSpell(SPELL_HUNTER_VICIOUS_VIPER); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_hun_viper_attack_speed::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_hun_viper_attack_speed::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 56841 - Glyph of Arcane Shot +class spell_hun_glyph_of_arcane_shot : public AuraScript +{ + PrepareAuraScript(spell_hun_glyph_of_arcane_shot); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HUNTER_GLYPH_OF_ARCANE_SHOT }); + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (Unit* procTarget = eventInfo.GetProcTarget()) + { + // Find Serpent Sting, Viper Sting, Scorpid Sting, Wyvern Sting + const auto found = std::find_if(std::begin(procTarget->GetAppliedAuras()), std::end(procTarget->GetAppliedAuras()), + [&](std::pair pair) + { + Aura const* aura = pair.second->GetBase(); + return ((aura->GetCasterGUID() == GetTarget()->GetGUID()) + && aura->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_HUNTER + && aura->GetSpellInfo()->SpellFamilyFlags.HasFlag(0xC000, 0x1080)); + }); + + if (found != std::end(procTarget->GetAppliedAuras())) + { + return true; + } + } + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + SpellInfo const* procSpell = eventInfo.GetSpellInfo(); + if (!procSpell) + { + return; + } + + int32 mana = procSpell->CalcPowerCost(GetTarget(), procSpell->GetSchoolMask()); + ApplyPct(mana, aurEff->GetAmount()); + + GetTarget()->CastCustomSpell(SPELL_HUNTER_GLYPH_OF_ARCANE_SHOT, SPELLVALUE_BASE_POINT0, mana, GetTarget()); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_hun_glyph_of_arcane_shot::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_hun_glyph_of_arcane_shot::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } +}; + +// -42243 - Volley (Trigger one) +class spell_hun_volley_trigger : public SpellScript +{ + PrepareSpellScript(spell_hun_volley_trigger); + + void SelectTarget(std::list& targets) + { + // It's here because Volley is an AOE spell so there is no specific target to be attacked + // Let's select one of our targets + if (!targets.empty()) + { + _target = *(targets.begin()); + } + } + + void HandleFinish() + { + if (!_target) + { + return; + } + + Unit* caster = GetCaster(); + if (!caster || !caster->IsPlayer()) + { + return; + } + + for (Unit::ControlSet::iterator itr = caster->m_Controlled.begin(); itr != caster->m_Controlled.end(); ++itr) + { + if (Unit* pet = *itr) + { + if (pet->IsAlive() && pet->GetTypeId() == TYPEID_UNIT) + { + pet->ToCreature()->AI()->OwnerAttacked(_target->ToUnit()); + } + } + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_hun_volley_trigger::SelectTarget, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY); + AfterCast += SpellCastFn(spell_hun_volley_trigger::HandleFinish); + } + +private: + WorldObject* _target = nullptr; +}; + +enum LocknLoadSpells +{ + SPELL_FROST_TRAP_SLOW = 67035 +}; + +// -56342 - Lock and Load +class spell_hun_lock_and_load : public AuraScript +{ + PrepareAuraScript(spell_hun_lock_and_load); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_LOCK_AND_LOAD_TRIGGER, SPELL_LOCK_AND_LOAD_MARKER, SPELL_FROST_TRAP_SLOW }); + } + + bool CheckTrapProc(ProcEventInfo& eventInfo) + { + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo || !eventInfo.GetActor()) + { + return false; + } + + // Black Arrow and Fire traps may trigger on periodic tick only. + if (((spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FIRE) || (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_SHADOW)) + && (spellInfo->Effects[0].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE || spellInfo->Effects[1].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE)) + { + return true; + } + + return IsTargetValid(spellInfo, eventInfo.GetProcTarget()) && !eventInfo.GetActor()->HasAura(SPELL_LOCK_AND_LOAD_MARKER); + } + + bool IsTargetValid(SpellInfo const* spellInfo, Unit* target) + { + if (!spellInfo || !target) + { + return false; + } + + // Don't check it for fire traps and black arrow, they proc on periodic only and not spell hit. + // So it's wrong to check for immunity, it was already checked when the spell was applied. + if ((spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FIRE) || (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_SHADOW)) + { + return false; + } + + // HitMask for Frost Trap can't be checked correctly as it is. + // That's because the talent is triggered by the spell that fires the trap (63487)... + // ...and not the actual spell that applies the slow effect (67035). + // So the IMMUNE result is never sent by the spell that triggers this. + if (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_NATURE) + { + if (SpellInfo const* triggerSpell = sSpellMgr->GetSpellInfo(SPELL_FROST_TRAP_SLOW)) + { + return !target->IsImmunedToSpell(triggerSpell); + } + } + + return true; + } + + template + void HandleProcs(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + + if (!(eventInfo.GetTypeMask() & mask) || !spellInfo) + { + return; + } + + // Also check if the proc from the fire traps and black arrow actually comes from the periodic ticks here. + // Normally this wouldn't be required, but we are circumventing the current proc system limitations. + if (((spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FIRE) || (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_SHADOW)) + && (spellInfo->Effects[0].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE || spellInfo->Effects[1].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) + && !(mask & PROC_FLAG_DONE_PERIODIC)) + { + return; + } + + if (!roll_chance_i(aurEff->GetAmount())) + { + return; + } + + Unit* caster = eventInfo.GetActor(); + caster->CastSpell(caster, SPELL_LOCK_AND_LOAD_TRIGGER, true); + } + + void ApplyMarker(ProcEventInfo& eventInfo) + { + if (IsTargetValid(eventInfo.GetSpellInfo(), eventInfo.GetProcTarget())) + { + Unit* caster = eventInfo.GetActor(); + caster->CastSpell(caster, SPELL_LOCK_AND_LOAD_MARKER, true); + } + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_hun_lock_and_load::CheckTrapProc); + + OnEffectProc += AuraEffectProcFn(spell_hun_lock_and_load::HandleProcs, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + OnEffectProc += AuraEffectProcFn(spell_hun_lock_and_load::HandleProcs, EFFECT_1, SPELL_AURA_DUMMY); + + AfterProc += AuraProcFn(spell_hun_lock_and_load::ApplyMarker); + } +}; + +// 19577 - Intimidation +class spell_hun_intimidation : public AuraScript +{ + PrepareAuraScript(spell_hun_intimidation); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + { + return !spellInfo->IsPositive(); + } + + return true; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_hun_intimidation::CheckProc); + } +}; + +// 19574 - Bestial Wrath +class spell_hun_bestial_wrath : public SpellScript +{ + PrepareSpellScript(spell_hun_bestial_wrath); + + SpellCastResult CheckCast() + { + Unit* caster = GetCaster(); + if (!caster || caster->GetTypeId() != TYPEID_PLAYER) + { + return SPELL_FAILED_NO_VALID_TARGETS; + } + + Pet* pet = caster->ToPlayer()->GetPet(); + if (!pet) + { + return SPELL_FAILED_NO_PET; + } + + if (!pet->IsAlive()) + { + SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_PET_IS_DEAD); + return SPELL_FAILED_CUSTOM_ERROR; + } + + return SPELL_CAST_OK; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_hun_bestial_wrath::CheckCast); + } +}; + +void AddSC_hunter_spell_scripts() +{ + RegisterSpellScript(spell_hun_check_pet_los); + RegisterSpellScript(spell_hun_cower); + RegisterSpellScript(spell_hun_wyvern_sting); + RegisterSpellScript(spell_hun_animal_handler); + RegisterSpellScript(spell_hun_generic_scaling); + RegisterSpellScript(spell_hun_taming_the_beast); + RegisterSpellScript(spell_hun_glyph_of_arcane_shot); + RegisterSpellScript(spell_hun_aspect_of_the_beast); + RegisterSpellScript(spell_hun_ascpect_of_the_viper); + RegisterSpellScript(spell_hun_chimera_shot); + RegisterSpellScript(spell_hun_disengage); + RegisterSpellScript(spell_hun_improved_mend_pet); + RegisterSpellScript(spell_hun_invigoration); + RegisterSpellScript(spell_hun_last_stand_pet); + RegisterSpellScript(spell_hun_masters_call); + RegisterSpellScript(spell_hun_misdirection); + RegisterSpellScript(spell_hun_misdirection_proc); + RegisterSpellScript(spell_hun_pet_carrion_feeder); + RegisterSpellScript(spell_hun_pet_heart_of_the_phoenix); + RegisterSpellScript(spell_hun_readiness); + RegisterSpellScript(spell_hun_scatter_shot); + RegisterSpellScript(spell_hun_sniper_training); + RegisterSpellScript(spell_hun_tame_beast); + RegisterSpellScript(spell_hun_viper_attack_speed); + RegisterSpellScript(spell_hun_volley_trigger); + RegisterSpellScript(spell_hun_lock_and_load); + RegisterSpellScript(spell_hun_intimidation); + RegisterSpellScript(spell_hun_bestial_wrath); +}