diff options
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/scripts/ExilesReach/zone_exiles_reach.cpp | 1650 | 
1 files changed, 1640 insertions, 10 deletions
diff --git a/src/server/scripts/ExilesReach/zone_exiles_reach.cpp b/src/server/scripts/ExilesReach/zone_exiles_reach.cpp index 9f1083c9cea..26bb9c5c3fe 100644 --- a/src/server/scripts/ExilesReach/zone_exiles_reach.cpp +++ b/src/server/scripts/ExilesReach/zone_exiles_reach.cpp @@ -15,6 +15,7 @@   * with this program. If not, see <http://www.gnu.org/licenses/>.   */ +#include "AreaTrigger.h"  #include "AreaTriggerAI.h"  #include "Conversation.h"  #include "CreatureAIImpl.h" @@ -30,11 +31,13 @@  #include "ScriptedCreature.h"  #include "ScriptMgr.h"  #include "ScriptSystem.h" +#include "SpellAuras.h"  #include "SpellInfo.h"  #include "SpellScript.h"  #include "TemporarySummon.h"  #include "Transport.h"  #include "Loot.h" +#include "SpellHistory.h"  template<class privateAI, class publicAI>  CreatureAI* GetPrivatePublicPairAISelector(Creature* creature) @@ -284,6 +287,8 @@ public:  // 325108 - Summon Throg - Combat Training (DNT)  class spell_summon_sparring_partner : public SpellScript  { +    // @TODO: drop after TARGET_UNK_142 impl +      void SelectTarget(WorldObject*& target)      {          Player* caster = GetCaster()->ToPlayer(); @@ -325,7 +330,7 @@ struct npc_sparring_partner_exiles_reach : public ScriptedAI              _actorId = ACTOR_ID_HORDE;              _actorIndex = 1;          } -        me->SetUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); +        me->SetImmuneToPC(true);          _events.ScheduleEvent(EVENT_MOVE_TO_A_POSITION, 1s);      } @@ -1521,10 +1526,10 @@ struct npc_lana_jordan_beach_laying : public ScriptedAI  enum ExilesReachMurlocsData  { -    ITEM_STITCHED_CLOTH_SHOES = 174791, -    ITEM_STITCHED_LEATHER_BOOTS = 174792, -    ITEM_LINKED_MAIL_BOOTS = 174793, -    ITEM_DENTED_PLATE_BOOTS = 174794, +    ITEM_STITCHED_CLOTH_SHOES           = 174791, +    ITEM_STITCHED_LEATHER_BOOTS         = 174792, +    ITEM_LINKED_MAIL_BOOTS              = 174793, +    ITEM_DENTED_PLATE_BOOTS             = 174794,      QUEST_MURLOC_HIDEAWAY_BOOTS_DROPPED = 58883  }; @@ -1836,7 +1841,7 @@ struct npc_garrick_summoned_beach : public ScriptedAI                  }                  case EVENT_FOLLOW_PLAYER:                      if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) -                        me->GetMotionMaster()->MoveFollow(player, 0.0f, 0.0f); +                        me->GetMotionMaster()->MoveFollow(player, 0.0f, float(M_PI / 4.0f));                      break;                  default:                      break; @@ -1933,7 +1938,7 @@ struct npc_grimaxe_summoned_beach : public ScriptedAI                  }                  case EVENT_FOLLOW_PLAYER:                      if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) -                        me->GetMotionMaster()->MoveFollow(player, 0.0f, 0.0f); +                        me->GetMotionMaster()->MoveFollow(player, 0.0f, float(M_PI / 4.0f));                      break;                  default:                      break; @@ -2018,6 +2023,8 @@ public:  // 325076 - Summon Warlord Grimaxe  class spell_summon_survivor_beach : public SpellScript  { +    // @TODO: drop after TARGET_UNK_142 impl +      void SelectTarget(WorldObject*& target)      {          Player* caster = GetCaster()->ToPlayer(); @@ -2186,6 +2193,1618 @@ struct areatrigger_find_the_lost_expedition_follower : AreaTriggerAI      }  }; +enum EnhancedCombatTacticsData +{ +    SPELL_SUMMON_CAPTAIN_GARRICK_COMBAT = 320211, +    SPELL_SUMMON_WARLORD_GRIMAXE_COMBAT = 325180 +}; + +// 59254 - Enhanced Combat Tactics (Alliance) +// 59339 - Enhanced Combat Tactics (Alliance Monk) +// 59933 - Enhanced Combat Tactics (Horde) +// 59934 - Enhanced Combat Tactics (Horde Monk) +class quest_enhanced_combat_tactics : public QuestScript +{ +public: +    quest_enhanced_combat_tactics() : QuestScript("quest_enhanced_combat_tactics") { } + +    void OnQuestStatusChange(Player* player, Quest const* /*quest*/, QuestStatus /*oldStatus*/, QuestStatus newStatus) override +    { +        switch (newStatus) +        { +            case QUEST_STATUS_INCOMPLETE: +                player->CastSpell(player, SPELL_UPDATE_PHASE_SHIFT); +                player->CastSpell(player, player->GetTeam() == ALLIANCE ? SPELL_SUMMON_CAPTAIN_GARRICK_COMBAT : SPELL_SUMMON_WARLORD_GRIMAXE_COMBAT); +                break; +            case QUEST_STATUS_NONE: +                player->RemoveAura(player->GetTeam() == ALLIANCE ? SPELL_SUMMON_CAPTAIN_GARRICK_COMBAT : SPELL_SUMMON_WARLORD_GRIMAXE_COMBAT); +                player->UpdateObjectVisibility(); +                player->CastSpell(player, SPELL_UPDATE_PHASE_SHIFT); +                break; +            default: +                break; +        } +    } +}; + +// 320175 - Summon Garrick - Combat Training (DNT) +// 325181 - Summon Grimaxe - Combat Training (DNT) +class spell_summon_combat_trainer : public SpellScript +{ +    // @TODO: drop after TARGET_UNK_142 impl + +    void SelectTarget(WorldObject*& target) +    { +        Player* caster = GetCaster()->ToPlayer(); +        if (!caster) +            return; + +        Creature* partner = FindCreatureIgnorePhase(caster, caster->GetTeam() == ALLIANCE ? "garrick_camp" : "grimaxe_camp", 10.0f); +        if (!partner) +            return; + +        target = partner; +    } + +    void Register() override +    { +        OnObjectTargetSelect += SpellObjectTargetSelectFn(spell_summon_combat_trainer::SelectTarget, EFFECT_0, TARGET_DEST_NEARBY_ENTRY_OR_DB); +    } +}; + +enum EnhancedCombatTrainerData +{ +    ACTOR_ID_ALLIANCE_ENHANCED_TRAINING                 = 74771, +    ACTOR_ID_HORDE_ENHANCED_TRAINING                    = 76285, + +    CHARGE_CATEGORY_CHARGE_SPELL                        = 1386, + +    CONVERSATION_PREFIGHT_WALK_ENHANCED                 = 13710, +    CONVERSATION_USE_SPELLS_AT_RANGE                    = 13630, +    CONVERSATION_READY_COMBAT                           = 14440, +    // Warrior +    CONVERSATION_READY_COMBAT_WARRIOR                   = 14441, +    CONVERSATION_CHARGE_ZERO_RES_ENHANCED               = 14444, +    CONVERSATION_SLAM_ENHANCED                          = 14447, +    CONVERSATION_CHARGE_ONE_RES_ENHANCED                = 14448, +    CONVERSATION_CHARGE_FINAL_ENHANCED                  = 14449, +    CONVERSATION_CHARGE_KICKBACK                        = 13611, +    // Paladin +    CONVERSATION_HOLY_POWER_ONE_PALADIN                 = 14452, +    CONVERSATION_CRUSADER_STRIKE_ONE_PALADIN            = 14453, +    CONVERSATION_CRUSADER_STRIKE_TWO_PALADIN            = 14454, +    CONVERSATION_SHIELD_SLAM_ONE_PALADIN                = 14455, +    CONVERSATION_HOLY_POWER_TWO_PALADIN                 = 14456, +    CONVERSATION_SHIELD_SLAM_TWO_PALADIN                = 14457, +    CONVERSATION_CRUSADER_STRIKE_THREE_PALADIN          = 14458, +    CONVERSATION_HOLY_POWER_THREE_PALADIN               = 14459, +    // Rogue +    CONVERSATION_SINISTER_STRIKE_ONE_ROGUE              = 14486, +    CONVERSATION_REGULAR_ATTACKS_ROGUE                  = 14487, +    CONVERSATION_THREE_COMBO_POINTS_ROGUE               = 14488, +    CONVERSATION_THREE_COMBO_EVISCERATE_ROGUE           = 14489, +    CONVERSATION_SINISTER_STRIKE_TWO_ROGUE              = 14490, +    CONVERSATION_FOUR_COMBO_POINTS_ROGUE                = 14491, +    CONVERSATION_FOUR_COMBO_EVISCERATE_ROGUE            = 14492, +    CONVERSATION_SINISTER_STRIKE_THREE_ROGUE            = 14493, +    CONVERSATION_FIVE_COMBO_POINTS_ROGUE                = 14494, +    CONVERSATION_FAILED_EVISCERATE_ROGUE                = 14495, +    // Priest +    CONVERSATION_SHADOW_WORD_PAIN_QUEST_CREDIT_PRIEST   = 13892, +    CONVERSATION_SMITE_PRE_COMBAT_PRIEST                = 14460, +    CONVERSATION_SHADOW_WORD_PAIN_PRE_COMBAT_PRIEST     = 14461, +    CONVERSATION_SHADOW_WORD_PAIN_TOO_SOON_PRIEST       = 14462, +    CONVERSATION_SHADOW_WORD_PAIN_FADING_PRIEST         = 14463, +    // Shaman +    CONVERSATION_LIGHTNINGBOLT_FIRST_SHAMAN             = 13631, +    CONVERSATION_PRIMAL_STRIKE_FIRST_SHAMAN             = 13632, +    CONVERSATION_PRIMAL_STRIKE_QUEST_CREDIT_SHAMAN      = 13633, +    CONVERSATION_LIGHTNINGBOLT_RANGE_SHAMAN             = 14475, +    // Mage +    CONVERSATION_FROSTBOLT_MAGE                         = 13634, +    CONVERSATION_FIRE_BLAST_QUEST_CREDIT_MAGE           = 13635, +    CONVERSATION_FROSTBOLT_CLOSE_MAGE                   = 14476, +    CONVERSATION_FIRE_BLAST_MAGE_NO_CREDIT              = 14477, +    // Warlock +    CONVERSATION_CORRUPTION_QUEST_CREDIT_WARLOCK        = 13895, +    CONVERSATION_SHADOW_BOLT_PRE_COMBAT_WARLOCK         = 14465, +    CONVERSATION_CORRUPTION_CAST_PRE_COMBAT_WARLOCK     = 14466, +    CONVERSATION_CORRUPTION_CAST_TOO_SOON_WARLOCK       = 14467, +    CONVERSATION_CORRUPTION_IS_FADING_WARLOCK           = 14468, +    // Druid +    CONVERSATION_MOONFIRE_QUEST_CREDIT_DRUID            = 13893, +    CONVERSATION_WRATH_PRE_COMBAT_DRUID                 = 14471, +    CONVERSATION_MOONFIRE_CAST_PRE_COMBAT_DRUID         = 14472, +    CONVERSATION_MOONFIRE_CAST_TOO_SOON_DRUID           = 14473, +    CONVERSATION_MOONFIRE_WEARING_OFF_DRUID             = 14474, + +    // All classes +    EVENT_COMBAT_TRAINING_WALK_AND_TALK                 = 1, +    EVENT_COMBAT_TRAINING_FACE_PLAYER, +    EVENT_COMBAT_RUN_BACK, +    EVENT_COMBAT_TRAINING_END, +    EVENT_COMBAT_CHECK_PLAYER, +    // Rogue +    EVENT_COMBAT_TRAINING_SINISTER_CHECK_ROGUE, +    // Priest, Warlock, Druid +    EVENT_COMBAT_TRAINING_SPELL_FADING, +    // Shaman +    EVENT_COMBAT_TRAINING_RESET_SHAMAN, +    EVENT_COMBAT_TRAINING_AGGRO_CHECK_SHAMAN, +    // Mage +    EVENT_COMBAT_TRAINING_RESET_MAGE, +    EVENT_COMBAT_TRAINING_AGGRO_CHECK_MAGE, + +    NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED              = 164577, +    NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED2             = 164775, +    //NPC_HORDE_SPARING_PARTNER_ENHANCED                  = 166916, +    NPC_INVISBUNNY_CAMP                                 = 167761, + +    PATH_COMBAT_TRAINER_HOME                            = 10512100, + +    POINT_WALK_POINT_ENHANCED_TRAINING                  = 1, +    POINT_RUN_POINT_ENHANCED_TRAINING                   = 2, +    POINT_TRAINING_POINT_ENHANCED_TRAINING              = 3, + +    QUEST_ENHANCED_COMBAT_TACTICS_ALLIANCE              = 59254, +    QUEST_ENHANCED_COMBAT_TACTICS_ALLIANCE_MONK         = 59339, +    QUEST_ENHANCED_COMBAT_TACTICS_HORDE                 = 59933, +    QUEST_ENHANCED_COMBAT_TACTICS_HORDE_MONK            = 59934, + +    QUEST_OBJECTIVE_HORDE_ABILITIES_PROVEN              = 397255, +    QUEST_OBJECTIVE_HORDE_TIGER_PALM                    = 397258, +    QUEST_OBJECTIVE_HORDE_BLACKOUT_KICK                 = 397259, +    QUEST_OBJECTIVE_ALLIANCE_ABILITIES_PROVEN           = 396220, +    QUEST_OBJECTIVE_ALLIANCE_TIGER_PALM                 = 396353, +    QUEST_OBJECTIVE_ALLIANCE_BLACKOUT_KICK              = 396354, + +    SPELL_DRINK_HEALING_POTION                          = 320229, +    SPELL_KNOCKBACK                                     = 320735, +    SPELL_CHARGE_KNOCKBACK_DRUID                        = 320767, +    SPELL_CHARGE                                        = 100, +    SPELL_SLAM                                          = 1464, +    SPELL_CHARGE_KNOCKBACK_WARRIOR                      = 320583, +    SPELL_SHIELD_OF_THE_RIGHTEOUS                       = 53600, +    SPELL_CRUSADER_STRIKE                               = 35395, +    SPELL_SINISTER_STRIKE                               = 1752, +    SPELL_EVISCERATE                                    = 196819, +    SPELL_SMITE                                         = 585, +    SPELL_SHADOW_WORD_PAIN                              = 589, +    SPELL_CHARGE_KNOCKBACK                              = 320605, +    SPELL_PRIMAL_STRIKE                                 = 73899, +    SPELL_LIGHTNING_BOLT                                = 188196, +    SPELL_FIRE_BLAST                                    = 319836, +    SPELL_FROSTBOLT                                     = 116, +    SPELL_CORRUPTION                                    = 172, +    SPELL_SHADOWBOLT                                    = 686, +    SPELL_MOONFIRE                                      = 164812, +    SPELL_WRATH                                         = 5176, +    SPELL_TIGER_PALM                                    = 100780, +    SPELL_BLACKOUT_KICK                                 = 100784, +    SPELL_RANGED_ROOT_DNT                               = 320608, +    SPELL_AGGRO_RADIUS_CHECK_DNT_WARRIOR                = 320741, +    SPELL_AGGRO_RADIUS_CHECK_DNT_PRIEST                 = 320649, +    SPELL_AGGRO_RADIUS_CHECK_DNT_SHAMAN                 = 320705, +    SPELL_AGGRO_RADIUS_CHECK_DNT_WARRIOR_MAGE           = 320741, +    SPELL_AGGRO_RADIUS_CHECK_DNT_WARLOCK                = 320606, +    SPELL_AGGRO_RADIUS_CHECK_DNT_DRUID                  = 320766 +}; + +Position const EnhancedTrainingWalkPosition = { -250.60243f, -2485.2517f, 17.787413f }; +Position const EnhancedTrainingRunPosition = { -231.5225f, -2480.5276f, 19.019197f }; + +// 164577 - Alliance Sparring Partner +// 166916 - Horde Sparring Partner +struct npc_sparring_partner_combat_training : public ScriptedAI +{ +    npc_sparring_partner_combat_training(Creature* creature) : ScriptedAI(creature), _questID(0), _summonSpellAuraID(0) { } + +    virtual void OnReadyPointReached() { } + +    virtual void HandleClassEvent(uint32 /*eventId*/) { } + +    void JustAppeared() override +    { +        _events.ScheduleEvent(EVENT_COMBAT_TRAINING_WALK_AND_TALK, 2s); +    } + +    void IsSummonedBy(WorldObject* summonerWO) override +    { +        Unit* summoner = summonerWO->ToUnit(); +        if (!summoner) +            return; + +        Player* player = summoner->ToPlayer(); +        if (!player) +            return; + +        _playerGUID = player->GetGUID(); + +        if (player->GetTeam() == ALLIANCE) +        { +            _summonSpellAuraID = SPELL_SUMMON_CAPTAIN_GARRICK_COMBAT; + +            if (player->GetClass() == CLASS_MONK) +                _questID = QUEST_ENHANCED_COMBAT_TACTICS_ALLIANCE_MONK; +            else +                _questID = QUEST_ENHANCED_COMBAT_TACTICS_ALLIANCE; +        } +        else +        { +            _summonSpellAuraID = SPELL_SUMMON_WARLORD_GRIMAXE_COMBAT; + +            if (player->GetClass() == CLASS_MONK) +                _questID = QUEST_ENHANCED_COMBAT_TACTICS_HORDE_MONK; +            else +                _questID = QUEST_ENHANCED_COMBAT_TACTICS_HORDE; +        } +    } + +    uint8 GetQuestCredits() +    { +        Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID); +        if (!player) +            return 0; + +        uint32 objectiveId = 0; +        switch (_questID) +        { +            case QUEST_ENHANCED_COMBAT_TACTICS_ALLIANCE: +                objectiveId = QUEST_OBJECTIVE_ALLIANCE_ABILITIES_PROVEN; +                break; +            case QUEST_ENHANCED_COMBAT_TACTICS_HORDE: +                objectiveId = QUEST_OBJECTIVE_HORDE_ABILITIES_PROVEN; +                break; +            default: +                break; +        } + +        return player->GetQuestSlotObjectiveData(_questID, objectiveId); +    } + +    void EnterEvadeMode(EvadeReason /*why*/) override +    { +        if (!me->IsAlive()) +            return; + +        me->CombatStop(true); +        EngagementOver(); +        me->ResetPlayerDamageReq(); +    } + +    void MovementInform(uint32 uiType, uint32 uiId) override +    { +        if (uiType != POINT_MOTION_TYPE) +            return; + +        switch (uiId) +        { +            case POINT_WALK_POINT_ENHANCED_TRAINING: +                me->SetWalk(false); +                me->GetMotionMaster()->MovePoint(POINT_RUN_POINT_ENHANCED_TRAINING, EnhancedTrainingRunPosition); +                break; +            case POINT_RUN_POINT_ENHANCED_TRAINING: +            { +                std::list<Creature*> sparpoints; +                GetCreatureListWithEntryInGrid(sparpoints, me, NPC_INVISBUNNY_CAMP, 100.0f); +                Trinity::Containers::RandomResize(sparpoints, 1); + +                for (Creature* creature : sparpoints) +                    me->GetMotionMaster()->MovePoint(POINT_TRAINING_POINT_ENHANCED_TRAINING, creature->GetPosition()); +                break; +            } +            case POINT_TRAINING_POINT_ENHANCED_TRAINING: +            { +                Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID); +                if (!player) +                    break; + +                me->SetFacingToObject(player); +                me->SetImmuneToPC(false); +                me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                _events.ScheduleEvent(EVENT_COMBAT_CHECK_PLAYER, 1s); + +                OnReadyPointReached(); +                break; +            } +            default: +                break; +        } +    } + +    void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override +    { +        damage = me->GetHealth() - 1; + +        if (me->HealthBelowPctDamaged(20, damage)) +            me->CastSpell(me, SPELL_DRINK_HEALING_POTION); +    } + +    void DamageDealt(Unit* target, uint32& damage, DamageEffectType /*damageType*/) override +    { +        if (target->GetHealthPct() < 91) +            damage = 0; +    } + +    void StartConversationWithPlayer(uint32 conversationId) +    { +        if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) +        { +            Conversation* conversation = Conversation::CreateConversation(conversationId, player, *player, player->GetGUID(), nullptr, false); +            if (!conversation) +                return; + +            conversation->AddActor(ACTOR_ID_ALLIANCE_ENHANCED_TRAINING, 0, player->GetTeam() == ALLIANCE ? me->GetGUID() : ObjectGuid::Empty); +            conversation->AddActor(ACTOR_ID_HORDE_ENHANCED_TRAINING, 1, player->GetTeam() == HORDE ? me->GetGUID() : ObjectGuid::Empty); +            conversation->Start(); +        } +    } + +    void WaypointPathEnded(uint32 /*nodeId*/, uint32 pathId) override +    { +        // Used to check if reached home +        if (pathId == PATH_COMBAT_TRAINER_HOME) +        { +            if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) +            { +                player->CastSpell(player, SPELL_UPDATE_PHASE_SHIFT); +                player->RemoveAura(_summonSpellAuraID); +            } +        } +    } + +    void UpdateAI(uint32 diff) override +    { +        _events.Update(diff); + +        while (uint32 eventId = _events.ExecuteEvent()) +        { +            switch (eventId) +            { +                case EVENT_COMBAT_TRAINING_WALK_AND_TALK: +                    // Used by all classes +                    me->SetWalk(true); +                    me->GetMotionMaster()->MovePoint(POINT_WALK_POINT_ENHANCED_TRAINING, EnhancedTrainingWalkPosition); +                    StartConversationWithPlayer(CONVERSATION_PREFIGHT_WALK_ENHANCED); +                    break; +                case EVENT_COMBAT_TRAINING_FACE_PLAYER: +                { +                    // Used by all classes +                    if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) +                        me->SetFacingToObject(player); + +                    me->SetImmuneToPC(false); +                    me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                    break; +                } +                case EVENT_COMBAT_TRAINING_END: +                    // Used by all classes +                    me->SetImmuneToPC(true); +                    me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                    me->RemoveAllAuras(); +                    if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) +                    { +                        me->SetFacingToObject(player); +                        StartConversationWithPlayer(CONVERSATION_CHARGE_FINAL_ENHANCED); +                        _events.ScheduleEvent(EVENT_COMBAT_RUN_BACK, 4s); +                    } +                    break; +                case EVENT_COMBAT_RUN_BACK: +                    // Used by all classes +                    me->GetMotionMaster()->Clear(); +                    me->GetMotionMaster()->MovePath(PATH_COMBAT_TRAINER_HOME, false); +                    break; +                default: +                    HandleClassEvent(eventId); +                    break; +            } +        } + +        if (!UpdateVictim()) +            return; + +        DoMeleeAttackIfReady(); +    } + +protected: +    uint32 _questID; +    uint32 _summonSpellAuraID; +    EventMap _events; +    ObjectGuid _playerGUID; +}; + +// 164577 - Alliance Sparring Partner +// 166916 - Horde Sparring Partner +struct npc_sparring_partner_enhanced_combat_training_warrior : public npc_sparring_partner_combat_training +{ +    npc_sparring_partner_enhanced_combat_training_warrior(Creature* creature) : npc_sparring_partner_combat_training(creature), _slamCounter(0), _secondaryCheck(false) { } + +    void ResetWarrior(Player* player) +    { +        _slamCounter = 0; +        me->SetImmuneToPC(true); +        me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +        player->GetSpellHistory()->ResetCharges(CHARGE_CATEGORY_CHARGE_SPELL); +        me->CastSpell(me, SPELL_AGGRO_RADIUS_CHECK_DNT_WARRIOR_MAGE); +        me->CastSpell(me, SPELL_RANGED_ROOT_DNT); +        _events.ScheduleEvent(EVENT_COMBAT_TRAINING_FACE_PLAYER, 1s); +        StartConversationWithPlayer(CONVERSATION_CHARGE_KICKBACK); +    } + +    void OnReadyPointReached() override +    { +        StartConversationWithPlayer(CONVERSATION_READY_COMBAT_WARRIOR); +        me->CastSpell(me, SPELL_AGGRO_RADIUS_CHECK_DNT_WARRIOR_MAGE); +        me->CastSpell(me, SPELL_RANGED_ROOT_DNT); +        _secondaryCheck = true; +    } + +    void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override +    { +        Player* player = caster->ToPlayer(); +        if (!player) +            return; + +        if (spellInfo->Id == SPELL_CHARGE) +        { +            me->RemoveAura(SPELL_RANGED_ROOT_DNT); +            me->RemoveAura(SPELL_AGGRO_RADIUS_CHECK_DNT_WARRIOR_MAGE); + +            player->KilledMonsterCredit(NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED); + +            switch (GetQuestCredits()) +            { +                case 1: +                    StartConversationWithPlayer(CONVERSATION_CHARGE_ZERO_RES_ENHANCED); +                    break; +                case 2: +                    StartConversationWithPlayer(CONVERSATION_CHARGE_ONE_RES_ENHANCED); +                    break; +                case 3: +                    _events.ScheduleEvent(EVENT_COMBAT_TRAINING_END, 1s); +                    break; +                default: +                    break; +            } +        } +        else if (spellInfo->Id == SPELL_SLAM) +        { +            ++_slamCounter; + +            if (_slamCounter == 3 && GetQuestCredits()) +                ResetWarrior(player); + +            if (!_secondaryCheck) +                return; + +            _secondaryCheck = false; +            StartConversationWithPlayer(CONVERSATION_SLAM_ENHANCED); +        } +    } + +private: +    uint8 _slamCounter; +    bool _secondaryCheck; +}; + +// 164577 - Alliance Sparring Partner +// 166916 - Horde Sparring Partner +struct npc_sparring_partner_enhanced_combat_training_paladin : public npc_sparring_partner_combat_training +{ +    npc_sparring_partner_enhanced_combat_training_paladin(Creature* creature) : npc_sparring_partner_combat_training(creature), _secondaryCheck(false), _holyPowerCheck(false) { } + +    void OnReadyPointReached() override +    { +        StartConversationWithPlayer(CONVERSATION_READY_COMBAT); +        _holyPowerCheck = true; +        _secondaryCheck = true; +    } + +    void HandleClassEvent(uint32 eventId) override +    { +        switch (eventId) +        { +            case EVENT_COMBAT_CHECK_PLAYER: +            { +                if (_holyPowerCheck) // Used by paladin +                { +                    if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) +                    { +                        if (player->GetPower(POWER_HOLY_POWER) >= 3) +                        { +                            switch (GetQuestCredits()) +                            { +                                case 0: +                                    StartConversationWithPlayer(CONVERSATION_HOLY_POWER_ONE_PALADIN); +                                    break; +                                case 1: +                                    StartConversationWithPlayer(CONVERSATION_HOLY_POWER_TWO_PALADIN); +                                    break; +                                case 2: +                                    StartConversationWithPlayer(CONVERSATION_HOLY_POWER_THREE_PALADIN); +                                    break; +                                default: +                                    break; +                            } +                            _holyPowerCheck = false; +                        } +                    } +                } +                _events.ScheduleEvent(EVENT_COMBAT_CHECK_PLAYER, 500ms); +                break; +            } +            default: +                break; +        } +    } + +    void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override +    { +        Player* player = caster->ToPlayer(); +        if (!player) +            return; + +        if (spellInfo->Id == SPELL_SHIELD_OF_THE_RIGHTEOUS) +        { +            player->KilledMonsterCredit(NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED); + +            switch (GetQuestCredits()) +            { +                case 1: +                    StartConversationWithPlayer(CONVERSATION_SHIELD_SLAM_ONE_PALADIN); +                    _secondaryCheck = true; +                    break; +                case 2: +                    StartConversationWithPlayer(CONVERSATION_SHIELD_SLAM_TWO_PALADIN); +                    _secondaryCheck = true; +                    break; +                case 3: +                    _events.ScheduleEvent(EVENT_COMBAT_TRAINING_END, 1s); +                    _events.CancelEvent(EVENT_COMBAT_CHECK_PLAYER); +                    break; +                default: +                    break; +            } +        } +        else if (spellInfo->Id == SPELL_CRUSADER_STRIKE) +        { +            if (_secondaryCheck) +            { +                switch (GetQuestCredits()) +                { +                    case 0: +                        StartConversationWithPlayer(CONVERSATION_CRUSADER_STRIKE_ONE_PALADIN); +                        break; +                    case 1: +                        StartConversationWithPlayer(CONVERSATION_CRUSADER_STRIKE_TWO_PALADIN); +                        break; +                    case 2: +                        StartConversationWithPlayer(CONVERSATION_CRUSADER_STRIKE_THREE_PALADIN); +                        break; +                    default: +                        break; +                } +                _secondaryCheck = false; +                _holyPowerCheck = true; +            } +        } +    } + +private: +    bool _secondaryCheck; +    bool _holyPowerCheck; +}; + +// 164577 - Alliance Sparring Partner +// 166916 - Horde Sparring Partner +struct npc_sparring_partner_enhanced_combat_training_rogue : public npc_sparring_partner_combat_training +{ +    npc_sparring_partner_enhanced_combat_training_rogue(Creature* creature) : npc_sparring_partner_combat_training(creature), _comboPointsCounter(0), _secondaryCheck(false), _comboPointCheck(false) { } + +    void OnReadyPointReached() override +    { +        StartConversationWithPlayer(CONVERSATION_READY_COMBAT); +        _comboPointCheck = true; +        _secondaryCheck = true; +    } + +    void JustEngagedWith(Unit* /*who*/) override +    { +        Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID); +        if (!player) +            return; + +        if (!GetQuestCredits()) +            _events.ScheduleEvent(EVENT_COMBAT_TRAINING_SINISTER_CHECK_ROGUE, 8s, 20s); +    } + +    void HandleClassEvent(uint32 eventId) override +    { +        switch (eventId) +        { +            case EVENT_COMBAT_TRAINING_SINISTER_CHECK_ROGUE: +                StartConversationWithPlayer(CONVERSATION_REGULAR_ATTACKS_ROGUE); +                _events.ScheduleEvent(EVENT_COMBAT_TRAINING_SINISTER_CHECK_ROGUE, 8s, 20s); +                break; +            case EVENT_COMBAT_CHECK_PLAYER: +            { +                if (_comboPointCheck) // Used by rogue +                { +                    if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) +                        _comboPointsCounter = player->GetPower(POWER_COMBO_POINTS); + +                    if (_comboPointsCounter >= (GetQuestCredits() + 3)) +                    { +                        switch (GetQuestCredits()) +                        { +                            case 0: +                                StartConversationWithPlayer(CONVERSATION_THREE_COMBO_POINTS_ROGUE); +                                break; +                            case 1: +                                StartConversationWithPlayer(CONVERSATION_FOUR_COMBO_POINTS_ROGUE); +                                break; +                            case 2: +                                StartConversationWithPlayer(CONVERSATION_FIVE_COMBO_POINTS_ROGUE); +                                break; +                            default: +                                break; +                        } +                        _comboPointCheck = false; +                    } +                } +                _events.ScheduleEvent(EVENT_COMBAT_CHECK_PLAYER, 500ms); +                break; +            } +            default: +                break; +        } +    } + +    void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override +    { +        Player* player = caster->ToPlayer(); +        if (!player) +            return; + +        if (spellInfo->Id == SPELL_EVISCERATE) +        { +            _comboPointCheck = true; +            if (_comboPointsCounter >= (GetQuestCredits() + 3)) +            { +                player->KilledMonsterCredit(NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED); + +                switch (GetQuestCredits()) +                { +                    case 1: +                        StartConversationWithPlayer(CONVERSATION_THREE_COMBO_EVISCERATE_ROGUE); +                        _secondaryCheck = true; +                        break; +                    case 2: +                        StartConversationWithPlayer(CONVERSATION_FOUR_COMBO_EVISCERATE_ROGUE); +                        _secondaryCheck = true; +                        break; +                    case 3: +                        _events.ScheduleEvent(EVENT_COMBAT_TRAINING_END, 1s); +                        _events.CancelEvent(EVENT_COMBAT_CHECK_PLAYER); +                        break; +                    default: +                        break; +                } +            } +            else +            { +                StartConversationWithPlayer(CONVERSATION_FAILED_EVISCERATE_ROGUE); +            } +        } +        else if (spellInfo->Id == SPELL_SINISTER_STRIKE) +        { +            if (!GetQuestCredits()) +                _events.RescheduleEvent(EVENT_COMBAT_TRAINING_SINISTER_CHECK_ROGUE, 8s, 20s); + +            if (_secondaryCheck) +            { +                switch (GetQuestCredits()) +                { +                    case 0: +                        StartConversationWithPlayer(CONVERSATION_SINISTER_STRIKE_ONE_ROGUE); +                        break; +                    case 1: +                        StartConversationWithPlayer(CONVERSATION_SINISTER_STRIKE_TWO_ROGUE); +                        break; +                    case 2: +                        StartConversationWithPlayer(CONVERSATION_SINISTER_STRIKE_THREE_ROGUE); +                        break; +                    default: +                        break; +                } +                _secondaryCheck = false; +            } +        } +    } + +private: +    uint8 _comboPointsCounter; +    bool _secondaryCheck; +    bool _comboPointCheck; +}; + +// 164577 - Alliance Sparring Partner +// 166916 - Horde Sparring Partner +struct npc_sparring_partner_enhanced_combat_training_priest : public npc_sparring_partner_combat_training +{ +    npc_sparring_partner_enhanced_combat_training_priest(Creature* creature) : npc_sparring_partner_combat_training(creature), _shadowWordPainInPandemicWindow(false), _secondaryCheck(false) { } + +    void OnReadyPointReached() override +    { +        StartConversationWithPlayer(CONVERSATION_READY_COMBAT); +        me->CastSpell(me, SPELL_AGGRO_RADIUS_CHECK_DNT_PRIEST); +        me->CastSpell(me, SPELL_RANGED_ROOT_DNT); +        _secondaryCheck = true; +    } + +    void HandleClassEvent(uint32 eventId) override +    { +        switch (eventId) +        { +            case EVENT_COMBAT_TRAINING_SPELL_FADING: +            { +                Aura* aura = me->GetAura(SPELL_MOONFIRE); +                if (!aura) +                    break; + +                if (!_shadowWordPainInPandemicWindow) +                { +                    int32 pandemicDuration = CalculatePct(aura->GetMaxDuration(), 30.0f); +                    if (aura->GetDuration() <= pandemicDuration) +                    { +                        _shadowWordPainInPandemicWindow = true; +                        StartConversationWithPlayer(CONVERSATION_SHADOW_WORD_PAIN_FADING_PRIEST); +                    } +                } +                _events.ScheduleEvent(EVENT_COMBAT_TRAINING_SPELL_FADING, 1s); +                break; +            } +            default: +                break; +        } +    } + +    void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override +    { +        Player* player = caster->ToPlayer(); +        if (!player) +            return; + +        if (spellInfo->Id == SPELL_SHADOW_WORD_PAIN) +        { +            if (_events.GetTimeUntilEvent(EVENT_COMBAT_TRAINING_SPELL_FADING) == Milliseconds::max()) +                _events.ScheduleEvent(EVENT_COMBAT_TRAINING_SPELL_FADING, 1s); + +            if (!_shadowWordPainInPandemicWindow) +            { +                if (_secondaryCheck) +                { +                    StartConversationWithPlayer(CONVERSATION_SHADOW_WORD_PAIN_PRE_COMBAT_PRIEST); +                    me->SetImmuneToPC(false); +                    me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                    me->RemoveAura(SPELL_RANGED_ROOT_DNT); +                    _secondaryCheck = false; +                } +                else +                    StartConversationWithPlayer(CONVERSATION_SHADOW_WORD_PAIN_TOO_SOON_PRIEST); +            } +            else +            { +                _shadowWordPainInPandemicWindow = false; +                player->KilledMonsterCredit(NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED); + +                if (player->GetQuestStatus(_questID) == QUEST_STATUS_COMPLETE) +                    _events.ScheduleEvent(EVENT_COMBAT_TRAINING_END, 1s); +                else +                    StartConversationWithPlayer(CONVERSATION_SHADOW_WORD_PAIN_QUEST_CREDIT_PRIEST); +            } +        } +        else if (spellInfo->Id == SPELL_SMITE) +        { +            if (_secondaryCheck) +                StartConversationWithPlayer(CONVERSATION_SMITE_PRE_COMBAT_PRIEST); +        } +    } + +private: +    bool _shadowWordPainInPandemicWindow; +    bool _secondaryCheck; +}; + +// 164577 - Alliance Sparring Partner +// 166916 - Horde Sparring Partner +struct npc_sparring_partner_enhanced_combat_training_shaman : public npc_sparring_partner_combat_training +{ +    npc_sparring_partner_enhanced_combat_training_shaman(Creature* creature) : npc_sparring_partner_combat_training(creature), _primalStrikeCounter(0), _secondaryCheck(false) { } + +    void OnReadyPointReached() override +    { +        StartConversationWithPlayer(CONVERSATION_READY_COMBAT); +        me->CastSpell(me, SPELL_AGGRO_RADIUS_CHECK_DNT_SHAMAN); +        me->CastSpell(me, SPELL_RANGED_ROOT_DNT); +        _secondaryCheck = true; +    } + +    void HandleClassEvent(uint32 eventId) override +    { +        switch (eventId) +        { +            case EVENT_COMBAT_TRAINING_RESET_SHAMAN: +                if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) +                { +                    me->CastSpell(player, SPELL_KNOCKBACK); +                    _events.ScheduleEvent(EVENT_COMBAT_TRAINING_AGGRO_CHECK_SHAMAN, 2s); +                } +                break; +            case EVENT_COMBAT_TRAINING_AGGRO_CHECK_SHAMAN: +                me->CastSpell(me, SPELL_AGGRO_RADIUS_CHECK_DNT_SHAMAN); +                me->SetImmuneToPC(false); +                me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                _secondaryCheck = true; +                break; +            default: +                break; +        } +    } + +    void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override +    { +        Player* player = caster->ToPlayer(); +        if (!player) +            return; + +        if (spellInfo->Id == SPELL_PRIMAL_STRIKE) +        { +            if (!player->IsWithinDist(me, 2.0f)) +                return; + +            ++_primalStrikeCounter; + +            if (_primalStrikeCounter < 3) +            { +                if (_primalStrikeCounter == 1) +                    StartConversationWithPlayer(CONVERSATION_PRIMAL_STRIKE_FIRST_SHAMAN); +                return; +            } + +            _primalStrikeCounter = 0; +            player->KilledMonsterCredit(NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED); + +            if (player->GetQuestStatus(_questID) == QUEST_STATUS_COMPLETE) +            { +                _events.ScheduleEvent(EVENT_COMBAT_TRAINING_END, 1s); +            } +            else +            { +                StartConversationWithPlayer(CONVERSATION_PRIMAL_STRIKE_QUEST_CREDIT_SHAMAN); +                me->SetImmuneToPC(true); +                me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                _events.ScheduleEvent(EVENT_COMBAT_TRAINING_RESET_SHAMAN, 3s); +            } +        } +        else if (spellInfo->Id == SPELL_LIGHTNING_BOLT) +        { +            me->RemoveAura(SPELL_RANGED_ROOT_DNT); +            me->RemoveAura(SPELL_AGGRO_RADIUS_CHECK_DNT_SHAMAN); +            if (_secondaryCheck) +            { +                StartConversationWithPlayer(CONVERSATION_LIGHTNINGBOLT_FIRST_SHAMAN); +                _secondaryCheck = false; +            } +            else +            { +                if (player->IsWithinDist(me, 2.0f)) +                    StartConversationWithPlayer(CONVERSATION_LIGHTNINGBOLT_RANGE_SHAMAN); +            } +        } +    } + +private: +    uint8 _primalStrikeCounter; +    bool _secondaryCheck; +}; + +// 164577 - Alliance Sparring Partner +// 166916 - Horde Sparring Partner +struct npc_sparring_partner_enhanced_combat_training_mage : public npc_sparring_partner_combat_training +{ +    npc_sparring_partner_enhanced_combat_training_mage(Creature* creature) : npc_sparring_partner_combat_training(creature), _secondaryCheck(true) { } + +    void OnReadyPointReached() override +    { +        StartConversationWithPlayer(CONVERSATION_READY_COMBAT); +        me->CastSpell(me, SPELL_AGGRO_RADIUS_CHECK_DNT_WARRIOR_MAGE); +        me->CastSpell(me, SPELL_RANGED_ROOT_DNT); +        _secondaryCheck = true; +    } + +    void HandleClassEvent(uint32 eventId) override +    { +        switch (eventId) +        { +            case EVENT_COMBAT_TRAINING_RESET_MAGE: +                if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) +                { +                    me->CastSpell(player, SPELL_KNOCKBACK); +                    _events.ScheduleEvent(EVENT_COMBAT_TRAINING_AGGRO_CHECK_MAGE, 2s); +                } +                break; +            case EVENT_COMBAT_TRAINING_AGGRO_CHECK_MAGE: +                me->CastSpell(me, SPELL_AGGRO_RADIUS_CHECK_DNT_WARRIOR_MAGE); +                me->SetImmuneToPC(false); +                me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                _secondaryCheck = true; +                break; +            default: +                break; +        } +    } + +    void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override +    { +        Player* player = caster->ToPlayer(); +        if (!player) +            return; + +        if (spellInfo->Id == SPELL_FIRE_BLAST) +        { +            if (player->IsWithinDist(me, 2.0f)) +            { +                player->KilledMonsterCredit(NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED); + +                if (player->GetQuestStatus(_questID) == QUEST_STATUS_COMPLETE) +                { +                    _events.ScheduleEvent(EVENT_COMBAT_TRAINING_END, 1s); +                } +                else +                { +                    StartConversationWithPlayer(CONVERSATION_FIRE_BLAST_QUEST_CREDIT_MAGE); +                    me->SetImmuneToPC(true); +                    me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                    _events.ScheduleEvent(EVENT_COMBAT_TRAINING_RESET_MAGE, 4s); +                } +            } +            else +            { +                StartConversationWithPlayer(CONVERSATION_FIRE_BLAST_MAGE_NO_CREDIT); +            } +        } +        else if (spellInfo->Id == SPELL_FROSTBOLT) +        { +            me->RemoveAura(SPELL_RANGED_ROOT_DNT); +            me->RemoveAura(SPELL_AGGRO_RADIUS_CHECK_DNT_WARRIOR_MAGE); +            if (_secondaryCheck) +            { +                StartConversationWithPlayer(CONVERSATION_FROSTBOLT_MAGE); +                _secondaryCheck = false; +            } +            else +            { +                if (player->IsWithinDist(me, 2.0f)) +                    StartConversationWithPlayer(CONVERSATION_FROSTBOLT_CLOSE_MAGE); +            } +        } +    } + +private: +    bool _secondaryCheck; +}; + +// 164577 - Alliance Sparring Partner +// 166916 - Horde Sparring Partner +struct npc_sparring_partner_enhanced_combat_training_warlock : public npc_sparring_partner_combat_training +{ +    npc_sparring_partner_enhanced_combat_training_warlock(Creature* creature) : npc_sparring_partner_combat_training(creature), _corruptionInPandemicWindow(false), _secondaryCheck(false) { } + +    void OnReadyPointReached() override +    { +        StartConversationWithPlayer(CONVERSATION_READY_COMBAT); +        me->CastSpell(me, SPELL_AGGRO_RADIUS_CHECK_DNT_WARLOCK); +        me->CastSpell(me, SPELL_RANGED_ROOT_DNT); +        _secondaryCheck = true; +    } + +    void HandleClassEvent(uint32 eventId) override +    { +        switch (eventId) +        { +            case EVENT_COMBAT_TRAINING_SPELL_FADING: +            { +                Aura* aura = me->GetAura(SPELL_MOONFIRE); +                if (!aura) +                    break; + +                if (!_corruptionInPandemicWindow) +                { +                    int32 pandemicDuration = CalculatePct(aura->GetMaxDuration(), 30.0f); +                    if (aura->GetDuration() <= pandemicDuration) +                    { +                        _corruptionInPandemicWindow = true; +                        StartConversationWithPlayer(CONVERSATION_CORRUPTION_IS_FADING_WARLOCK); +                    } +                } +                _events.ScheduleEvent(EVENT_COMBAT_TRAINING_SPELL_FADING, 1s); +                break; +            } +            default: +                break; +        } +    } + +    void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override +    { +        Player* player = caster->ToPlayer(); +        if (!player) +            return; + +        if (spellInfo->Id == SPELL_CORRUPTION) +        { +            if (_events.GetTimeUntilEvent(EVENT_COMBAT_TRAINING_SPELL_FADING) == Milliseconds::max()) +                _events.ScheduleEvent(EVENT_COMBAT_TRAINING_SPELL_FADING, 1s); + +            if (!_corruptionInPandemicWindow) +            { +                if (_secondaryCheck) +                { +                    StartConversationWithPlayer(CONVERSATION_CORRUPTION_CAST_PRE_COMBAT_WARLOCK); +                    me->SetImmuneToPC(false); +                    me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                    me->RemoveAura(SPELL_RANGED_ROOT_DNT); +                    _secondaryCheck = false; +                } +                else +                    StartConversationWithPlayer(CONVERSATION_CORRUPTION_CAST_TOO_SOON_WARLOCK); +            } +            else +            { +                _corruptionInPandemicWindow = false; +                player->KilledMonsterCredit(NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED); + +                if (player->GetQuestStatus(_questID) == QUEST_STATUS_COMPLETE) +                    _events.ScheduleEvent(EVENT_COMBAT_TRAINING_END, 1s); +                else +                    StartConversationWithPlayer(CONVERSATION_CORRUPTION_QUEST_CREDIT_WARLOCK); +            } +        } +        else if (spellInfo->Id == SPELL_SHADOWBOLT) +        { +            if (_secondaryCheck) +                StartConversationWithPlayer(CONVERSATION_SHADOW_BOLT_PRE_COMBAT_WARLOCK); +        } +    } + +private: +    bool _corruptionInPandemicWindow; +    bool _secondaryCheck; +}; + +// 164577 - Alliance Sparring Partner +// 166916 - Horde Sparring Partner +struct npc_sparring_partner_enhanced_combat_training_monk : public npc_sparring_partner_combat_training +{ +    npc_sparring_partner_enhanced_combat_training_monk(Creature* creature) : npc_sparring_partner_combat_training(creature) { } + +    void OnReadyPointReached() override +    { +        StartConversationWithPlayer(CONVERSATION_READY_COMBAT); +    } + +    void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override +    { +        Player* player = caster->ToPlayer(); +        if (!player) +            return; + +        if (spellInfo->Id == SPELL_TIGER_PALM) +            player->KilledMonsterCredit(NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED); +        else if (spellInfo->Id == SPELL_BLACKOUT_KICK) +            player->KilledMonsterCredit(NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED2); + +        if (player->GetQuestStatus(_questID) == QUEST_STATUS_COMPLETE) +            _events.ScheduleEvent(EVENT_COMBAT_TRAINING_END, 1s); +    } +}; + +// 164577 - Alliance Sparring Partner +// 166916 - Horde Sparring Partner +struct npc_sparring_partner_enhanced_combat_training_druid : public npc_sparring_partner_combat_training +{ +    npc_sparring_partner_enhanced_combat_training_druid(Creature* creature) : npc_sparring_partner_combat_training(creature), _hitByMoonfire(false), _moonfireInPandemicWindow(false) { } + +    void OnReadyPointReached() override +    { +        StartConversationWithPlayer(CONVERSATION_READY_COMBAT); +        me->CastSpell(me, SPELL_AGGRO_RADIUS_CHECK_DNT_DRUID); +        me->CastSpell(me, SPELL_RANGED_ROOT_DNT); +        _moonfireInPandemicWindow = false; +        _hitByMoonfire = false; +    } + +    void HandleClassEvent(uint32 eventId) override +    { +        switch (eventId) +        { +            case EVENT_COMBAT_TRAINING_SPELL_FADING: +            { +                Aura* aura = me->GetAura(SPELL_MOONFIRE); +                if (!aura) +                    break; + +                if (!_moonfireInPandemicWindow) +                { +                    int32 pandemicDuration = CalculatePct(aura->GetMaxDuration(), 30.0f); +                    if (aura->GetDuration() <= pandemicDuration) +                    { +                        _moonfireInPandemicWindow = true; +                        StartConversationWithPlayer(CONVERSATION_MOONFIRE_WEARING_OFF_DRUID); +                    } +                } +                _events.ScheduleEvent(EVENT_COMBAT_TRAINING_SPELL_FADING, 1s); +                break; +            } +            default: +                break; +        } +    } + +    void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override +    { +        Player* player = caster->ToPlayer(); +        if (!player) +            return; + +        if (spellInfo->Id == SPELL_MOONFIRE) +        { +            if (_events.GetTimeUntilEvent(EVENT_COMBAT_TRAINING_SPELL_FADING) == Milliseconds::max()) +                _events.ScheduleEvent(EVENT_COMBAT_TRAINING_SPELL_FADING, 1s); + +            if (!_moonfireInPandemicWindow) +            { +                if (!_hitByMoonfire) +                { +                    StartConversationWithPlayer(CONVERSATION_MOONFIRE_CAST_PRE_COMBAT_DRUID); +                    me->SetImmuneToPC(false); +                    me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                    me->RemoveAura(SPELL_RANGED_ROOT_DNT); +                    _hitByMoonfire = true; +                } +                else +                    StartConversationWithPlayer(CONVERSATION_MOONFIRE_CAST_TOO_SOON_DRUID); +            } +            else +            { +                _moonfireInPandemicWindow = false; +                player->KilledMonsterCredit(NPC_ALLIANCE_SPARRING_PARTNER_ENHANCED); + +                if (player->GetQuestStatus(_questID) == QUEST_STATUS_COMPLETE) +                    _events.ScheduleEvent(EVENT_COMBAT_TRAINING_END, 1s); +                else +                    StartConversationWithPlayer(CONVERSATION_MOONFIRE_QUEST_CREDIT_DRUID); +            } +        } +        else if (spellInfo->Id == SPELL_WRATH) +        { +            if (!_hitByMoonfire) +                StartConversationWithPlayer(CONVERSATION_WRATH_PRE_COMBAT_DRUID); +        } +    } + +private: +    bool _hitByMoonfire; +    bool _moonfireInPandemicWindow; +}; + +CreatureAI* SparringPartnerEnhancedCombatTrainingSelector(Creature* creature) +{ +    TempSummon* summon = creature->ToTempSummon(); +    if (!summon) +        return new NullCreatureAI(creature); + +    Unit* summoner = summon->GetSummonerUnit(); +    if (!summoner) +        return new NullCreatureAI(creature); + +    Player* player = summoner->ToPlayer(); +    if (!player) +        return new NullCreatureAI(creature); + +    switch (player->GetClass()) +    { +        case CLASS_WARRIOR: +            return new npc_sparring_partner_enhanced_combat_training_warrior(creature); +        case CLASS_PALADIN: +            return new npc_sparring_partner_enhanced_combat_training_paladin(creature); +        case CLASS_ROGUE: +            return new npc_sparring_partner_enhanced_combat_training_rogue(creature); +        case CLASS_PRIEST: +            return new npc_sparring_partner_enhanced_combat_training_priest(creature); +        case CLASS_SHAMAN: +            return new npc_sparring_partner_enhanced_combat_training_shaman(creature); +        case CLASS_MAGE: +            return new npc_sparring_partner_enhanced_combat_training_mage(creature); +        case CLASS_WARLOCK: +            return new npc_sparring_partner_enhanced_combat_training_warlock(creature); +        case CLASS_MONK: +            return new npc_sparring_partner_enhanced_combat_training_monk(creature); +        case CLASS_DRUID: +            return new npc_sparring_partner_enhanced_combat_training_druid(creature); +        default: +            return new NullCreatureAI(creature); +    } +    if (creature->IsPrivateObject()) +        return new npc_survivors_beach_leave_private<PATH_KEE_LA_STANDING, 7 * IN_MILLISECONDS>(creature); +    return new NullCreatureAI(creature); +}; + +struct at_aggro_radius_check_enhanced_combat_tactics : AreaTriggerAI +{ +    at_aggro_radius_check_enhanced_combat_tactics(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } + +    void OnUnitEnter(Unit* unit) override +    { +        Player* player = unit->ToPlayer(); +        if (!player) +            return; + +        Unit* caster = at->GetCaster(); +        if (!caster) +        { +            at->RemoveFromWorld(); +            return; +        } + +        caster->SetFacingToObject(player); + +        switch (player->GetClass()) +        { +            case CLASS_WARRIOR: +                caster->CastSpell(player, SPELL_CHARGE_KNOCKBACK_WARRIOR); +                break; +            case CLASS_PRIEST: +            case CLASS_SHAMAN: +            case CLASS_MAGE: +            case CLASS_WARLOCK: +                caster->CastSpell(player, SPELL_CHARGE_KNOCKBACK); +                break; +            case CLASS_DRUID: +                caster->CastSpell(player, SPELL_CHARGE_KNOCKBACK_DRUID); +                break; +            default: +                break; +        } +    } +}; + +// 320605 - Charge Knockback (DNT) +class spell_knockback_charge_enhanced_training : public SpellScript +{ +    void HandleLaunch(SpellEffIndex effIndex) +    { +        PreventHitDefaultEffect(effIndex); +    } + +    void HandleEffect(SpellEffIndex effIndex) +    { +        Unit* caster = GetCaster(); +        if (!caster) +            return; + +        Player* player = GetHitUnit()->ToPlayer(); +        if (!player) +            return; + +        Conversation* conversation = Conversation::CreateConversation(GetSpellInfo()->GetEffect(effIndex).MiscValue, player, *player, player->GetGUID(), nullptr, false); +        if (!conversation) +            return; + +        conversation->AddActor(ACTOR_ID_ALLIANCE_ENHANCED_TRAINING, 0, player->GetTeam() == ALLIANCE ? caster->GetGUID() : ObjectGuid::Empty); +        conversation->AddActor(ACTOR_ID_HORDE_ENHANCED_TRAINING, 1, player->GetTeam() == ALLIANCE ? ObjectGuid::Empty : caster->GetGUID()); +        conversation->Start(); +    } + +    void Register() override +    { +        OnEffectLaunchTarget += SpellEffectFn(spell_knockback_charge_enhanced_training::HandleLaunch, EFFECT_1, SPELL_EFFECT_CREATE_PRIVATE_CONVERSATION); +        OnEffectHitTarget += SpellEffectFn(spell_knockback_charge_enhanced_training::HandleEffect, EFFECT_1, SPELL_EFFECT_CREATE_PRIVATE_CONVERSATION); +    } +}; + +enum NorthboundData +{ +    CONVERSATION_QUEST_NORTHBOUND_ACCEPT_ALLIANCE       = 12066, +    CONVERSATION_QUEST_NORTHBOUND_ACCEPT_HORDE          = 14499, + +    POINT_LEADER_RUN                                    = 0, + +    ACTOR_ID_0_NORTHBOUND_ACCEPT_ALLIANCE               = 71310, +    ACTOR_ID_1_NORTHBOUND_ACCEPT_ALLIANCE               = 71297, +    ACTOR_ID_0_NORTHBOUND_ACCEPT_HORDE                  = 79890, +    ACTOR_ID_1_NORTHBOUND_ACCEPT_HORDE                  = 79888, +    ACTOR_ID_0_NORTHBOUND_AREATRIGGER_ALLIANCE          = 71317, +    ACTOR_ID_1_NORTHBOUND_AREATRIGGER_HORDE             = 76319, + +    QUEST_NORTHBOND_ALLIANCE                            = 55173, +    QUEST_NORTHBOND_HORDE                               = 59935, + +    SPELL_SUMMON_ADMIRAL_GARRICK_GUARDIAN_NORTHBOUND    = 305660, +    SPELL_SUMMON_WARLORD_GRIMAXE_GUARDIAN_NORTHBOUND    = 344382, +    SPELL_LINGER_NORTHBOUND_ALLIANCE                    = 305665, +    SPELL_LINGER_NORTHBOUND_HORDE                       = 344385, +}; + +Position const GarrickQuillboarBriarpatchPosition = { -142.62154f, -2641.0364f, 48.775497f }; +Position const GrimaxeQuillboarBriarpatchPosition = { -142.56076f, -2640.9915f, 48.755478f }; + +// 165360 - Alliance Survivor +// This script is used by Captian Garrick Follower for Northbound quest +struct npc_leader_northbound : public ScriptedAI +{ +    npc_leader_northbound(Creature* creature) : ScriptedAI(creature), _conversationId(0), _actorIdOne(0), _actorIdTwo(0), _lingerSpellId(0), _guardianSpellId(0) {} + +    void JustAppeared() override +    { +        Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID); +        if (!player) +            return; + +        player->UpdateVisibilityForPlayer(); + +        Creature* survivor = FindCreatureIgnorePhase(player, player->GetTeam() == ALLIANCE ? "alaria_standing_abandoned_camp" : "wonza_standing_abandoned_camp", 5.0f); +        if (!survivor) +            return; + +        if (player->GetTeam() == ALLIANCE) +        { +            _conversationId = CONVERSATION_QUEST_NORTHBOUND_ACCEPT_ALLIANCE; +            _actorIdOne = ACTOR_ID_0_NORTHBOUND_ACCEPT_ALLIANCE; +            _actorIdTwo = ACTOR_ID_1_NORTHBOUND_ACCEPT_ALLIANCE; +            _runToPosition = GarrickQuillboarBriarpatchPosition; +            _lingerSpellId = SPELL_LINGER_NORTHBOUND_ALLIANCE; +            _guardianSpellId = SPELL_SUMMON_ADMIRAL_GARRICK_GUARDIAN_NORTHBOUND; +        } +        else +        { +            _conversationId = CONVERSATION_QUEST_NORTHBOUND_ACCEPT_HORDE; +            _actorIdOne = ACTOR_ID_0_NORTHBOUND_ACCEPT_HORDE; +            _actorIdTwo = ACTOR_ID_1_NORTHBOUND_ACCEPT_HORDE; +            _runToPosition = GrimaxeQuillboarBriarpatchPosition; +            _lingerSpellId = SPELL_LINGER_NORTHBOUND_HORDE; +            _guardianSpellId = SPELL_SUMMON_WARLORD_GRIMAXE_GUARDIAN_NORTHBOUND; +        } + +        Conversation* conversation = Conversation::CreateConversation(_conversationId, player, *player, player->GetGUID(), nullptr, false); +        if (!conversation) +            return; + +        conversation->AddActor(0, 0, player->GetGUID()); +        conversation->AddActor(_actorIdOne, 1, me->GetGUID()); +        conversation->AddActor(_actorIdTwo, 2, survivor->GetGUID()); +        conversation->Start(); +    } + +    void IsSummonedBy(WorldObject* summoner) override +    { +        Player* player = summoner->ToPlayer(); +        if (!player) +            return; + +        _playerGUID = player->GetGUID(); + +        _events.ScheduleEvent(EVENT_FOLLOW_PLAYER, 3s); +    } + +    void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override +    { +        if (spellInfo->Id == _lingerSpellId) +        { +            me->GetMotionMaster()->Remove(FOLLOW_MOTION_TYPE); +            me->GetMotionMaster()->MovePoint(POINT_LEADER_RUN, _runToPosition, false); +        } +        return; +    } + +    void MovementInform(uint32 uiType, uint32 uiId) override +    { +        if (uiType != POINT_MOTION_TYPE) +            return; + +        if (uiId != POINT_LEADER_RUN) +            return; + +        me->SetFacingTo(6.0737457275390625); + +        if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) +        { +            player->CastSpell(player, SPELL_UPDATE_PHASE_SHIFT); +            player->RemoveAura(_guardianSpellId); +            player->UpdateVisibilityForPlayer(); +        } +    } + +    void UpdateAI(uint32 diff) override +    { +        _events.Update(diff); + +        while (uint32 eventId = _events.ExecuteEvent()) +        { +            switch (eventId) +            { +                case EVENT_FOLLOW_PLAYER: +                    if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) +                        me->GetMotionMaster()->MoveFollow(player, 0.0f, float(M_PI / 4.0f)); +                    break; +                default: +                    break; +            } +        } +    } + +private: +    EventMap _events; +    ObjectGuid _playerGUID; +    uint32 _conversationId; +    uint32 _actorIdOne; +    uint32 _actorIdTwo; +    Position _runToPosition; +    uint32 _lingerSpellId; +    uint32 _guardianSpellId; +}; + +// 55173 - Northbound +// 59935 - Northbound +class quest_northbound : public QuestScript +{ +public: +    quest_northbound(char const* script) : QuestScript(script) { } + +    void HandleQuestStatusChange(Player* player, QuestStatus newStatus, uint32 summonSpellId) +    { +        switch (newStatus) +        { +            case QUEST_STATUS_INCOMPLETE: +                player->CastSpell(player, SPELL_UPDATE_PHASE_SHIFT); +                player->CastSpell(player, summonSpellId); +                break; +            case QUEST_STATUS_NONE: +                player->RemoveAura(summonSpellId); +                player->CastSpell(player, SPELL_UPDATE_PHASE_SHIFT); +                player->UpdateVisibilityForPlayer(); +                break; +            default: +                break; +        } +    } +}; + +// 55173 - Northbound +class quest_northbound_alliance : public quest_northbound +{ +public: +    quest_northbound_alliance() : quest_northbound("quest_northbound_alliance") { } + +    void OnQuestStatusChange(Player* player, Quest const* /*quest*/, QuestStatus /*oldStatus*/, QuestStatus newStatus) override +    { +        HandleQuestStatusChange(player, newStatus, SPELL_SUMMON_ADMIRAL_GARRICK_GUARDIAN_NORTHBOUND); +    } +}; + +// 59935 - Northbound +class quest_northbound_horde : public quest_northbound +{ +public: +    quest_northbound_horde() : quest_northbound("quest_northbound_horde") { } + +    void OnQuestStatusChange(Player* player, Quest const* /*quest*/, QuestStatus /*oldStatus*/, QuestStatus newStatus) override +    { +        HandleQuestStatusChange(player, newStatus, SPELL_SUMMON_WARLORD_GRIMAXE_GUARDIAN_NORTHBOUND); +    } +}; + +// 305661 - Summon Admiral Garrick Guardian Summons Alliance Entry: 165360 +// 344383 - Summon Admiral Garrick Guardian Summons Horde Entry: 175034 +class spell_summon_leader_northbound : public SpellScript +{ +    // @TODO: drop after TARGET_UNK_142 impl + +    void SelectTarget(WorldObject*& target) +    { +        Player* caster = GetCaster()->ToPlayer(); +        if (!caster) +            return; + +        Creature* survivor = FindCreatureIgnorePhase(caster, caster->GetTeam() == ALLIANCE ? "garrick_camp" : "grimaxe_camp", 5.0f); +        if (!survivor) +            return; + +        target = survivor; +    } + +    void Register() override +    { +        OnObjectTargetSelect += SpellObjectTargetSelectFn(spell_summon_leader_northbound::SelectTarget, EFFECT_0, TARGET_DEST_NEARBY_ENTRY_OR_DB); +    } +}; + +struct at_northbound_linger : AreaTriggerAI +{ +    at_northbound_linger(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { } + +    void OnUnitEnter(Unit* unit) override +    { +        Player* player = unit->ToPlayer(); +        if (!player) +            return; + +        if (player->GetTeam() == ALLIANCE) +        { +            if (player->GetQuestStatus(QUEST_NORTHBOND_ALLIANCE) != QUEST_STATUS_COMPLETE) +                return; + +            if (!player->HasAura(SPELL_SUMMON_ADMIRAL_GARRICK_GUARDIAN_NORTHBOUND)) +                return; + +            if (player->HasAura(SPELL_LINGER_NORTHBOUND_ALLIANCE)) +                return; + +            player->CastSpell(player, SPELL_LINGER_NORTHBOUND_ALLIANCE); +        } +        else +        { +            if (player->GetQuestStatus(QUEST_NORTHBOND_HORDE) != QUEST_STATUS_COMPLETE) +                return; + +            if (!player->HasAura(SPELL_SUMMON_WARLORD_GRIMAXE_GUARDIAN_NORTHBOUND)) +                return; + +            if (player->HasAura(SPELL_LINGER_NORTHBOUND_HORDE)) +                return; + +            player->CastSpell(player, SPELL_LINGER_NORTHBOUND_HORDE); +        } +    } +}; + +// @TODO: drop +// 305665 - Scene Linger (DNT) +// 344385 - Scene Linger (DNT) +class spell_scene_linger_northbound: public SpellScript +{ +    void HandleLaunch(SpellEffIndex effIndex) +    { +        PreventHitDefaultEffect(effIndex); +    } + +    void HandleEffect(SpellEffIndex effIndex) +    { +        Player* player = GetHitUnit()->ToPlayer(); +        if (!player) +            return; + +        Creature* scout = FindCreatureIgnorePhase(player, player->GetTeam() == ALLIANCE ? "huxworth_briarpatch" : "dawntracker_briarpatch", 100.0f); +        if (!scout) +            return; + +        Conversation* conversation = Conversation::CreateConversation(GetSpellInfo()->GetEffect(effIndex).MiscValue, player, *player, player->GetGUID(), nullptr, false); +        if (!conversation) +            return; + +        conversation->AddActor(ACTOR_ID_0_NORTHBOUND_AREATRIGGER_ALLIANCE, 0, player->GetTeam() == ALLIANCE ? scout->GetGUID() : ObjectGuid::Empty); +        conversation->AddActor(ACTOR_ID_1_NORTHBOUND_AREATRIGGER_HORDE, 1, player->GetTeam() == ALLIANCE ? ObjectGuid::Empty : scout->GetGUID()); +        conversation->Start(); +    } + +    void Register() override +    { +        OnEffectLaunchTarget += SpellEffectFn(spell_scene_linger_northbound::HandleLaunch, EFFECT_2, SPELL_EFFECT_CREATE_PRIVATE_CONVERSATION); +        OnEffectHitTarget += SpellEffectFn(spell_scene_linger_northbound::HandleEffect, EFFECT_2, SPELL_EFFECT_CREATE_PRIVATE_CONVERSATION); +    } +}; +  void AddSC_zone_exiles_reach()  {      // Ship @@ -2205,7 +3824,6 @@ void AddSC_zone_exiles_reach()      new quest_brace_for_impact();      new player_exiles_reach_ship_crash();      new scene_alliance_and_horde_ship(); -      // Beach      RegisterSpellScript(spell_knocked_down_exiles_reach_beach);      new scene_alliance_and_horde_crash(); @@ -2235,7 +3853,6 @@ void AddSC_zone_exiles_reach()      new quest_finding_the_lost_expedition_alliance();      new quest_finding_the_lost_expedition_horde();      RegisterSpellScript(spell_summon_survivor_beach); -      // Abandoned Camp      new GenericCreatureScript<npc_captain_abandoned_camp_exiles_reach<QUEST_COOKING_MEAT_ALLIANCE, CONVERSATION_QUEST_COOKING_MEAT_ACCEPT_ALLIANCE>>("npc_captain_garrick_abandoned_camp");      new GenericCreatureScript<npc_captain_abandoned_camp_exiles_reach<QUEST_COOKING_MEAT_HORDE, CONVERSATION_QUEST_COOKING_MEAT_ACCEPT_HORDE>>("npc_warlord_grimaxe_abandoned_camp"); @@ -2243,4 +3860,17 @@ void AddSC_zone_exiles_reach()      new quest_cooking_meat_horde();      RegisterAreaTriggerAI(areatrigger_find_the_lost_expedition);      RegisterAreaTriggerAI(areatrigger_find_the_lost_expedition_follower); -} +    // Quest Enhanced Combat Tactics +    new quest_enhanced_combat_tactics(); +    RegisterSpellScript(spell_summon_combat_trainer); +    new FactoryCreatureScript<CreatureAI, &SparringPartnerEnhancedCombatTrainingSelector>("npc_sparring_partner_combat_training"); +    RegisterAreaTriggerAI(at_aggro_radius_check_enhanced_combat_tactics); +    RegisterSpellScript(spell_knockback_charge_enhanced_training); +    // Quest Northbound +    RegisterCreatureAI(npc_leader_northbound); +    new quest_northbound_alliance(); +    new quest_northbound_horde(); +    RegisterSpellScript(spell_summon_leader_northbound); +    RegisterAreaTriggerAI(at_northbound_linger); +    RegisterSpellScript(spell_scene_linger_northbound); +};  | 
