diff options
Diffstat (limited to 'src/server/scripts')
44 files changed, 1741 insertions, 710 deletions
diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index b35e71dbd50..30ee8254f72 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -374,6 +374,12 @@ public: if (show == "del") { + if (!arg_id) + { + handler->SendSysMessage("|cffff33ffERROR: Waypoint script guid not present.|r"); + return true; + } + id = atoi(arg_id); stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID); diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp index 0b5c3302890..96c7c5d8102 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp @@ -108,7 +108,7 @@ public: { Talk(SAY_DEATH); if (Unit* midnight = ObjectAccessor::GetUnit(*me, Midnight)) - midnight->Kill(midnight); + midnight->KillSelf(); } void UpdateAI(uint32 diff) override; @@ -264,7 +264,7 @@ void boss_attumen::boss_attumenAI::UpdateAI(uint32 diff) } Midnight.Clear(); me->SetVisible(false); - me->Kill(me); + me->KillSelf(); } else ResetTimer -= diff; } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp index 0abba3dfff4..d11c4a6e584 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp @@ -295,7 +295,7 @@ public: { Unit* axe = ObjectAccessor::GetUnit(*me, axes[i]); if (axe && axe->IsAlive()) - axe->Kill(axe); + axe->KillSelf(); axes[i].Clear(); } } diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp index a95ac9aaa20..e4cc7c0e84a 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp @@ -473,7 +473,7 @@ public: if (FlameStrikeTimer <= diff) { DoCast(me, SPELL_FLAMESTRIKE1_NORMAL, true); - me->Kill(me); + me->KillSelf(); } else FlameStrikeTimer -= diff; } }; @@ -636,7 +636,7 @@ public: if (HatchTimer <= diff) { me->SummonCreature(CREATURE_PHOENIX, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); - me->Kill(me); + me->KillSelf(); } else HatchTimer -= diff; } }; @@ -675,7 +675,7 @@ public: void UpdateAI(uint32 diff) override { if (DespawnTimer <= diff) - me->Kill(me); + me->KillSelf(); else DespawnTimer -= diff; diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp index f57de5316ab..480fd8848f5 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp @@ -139,7 +139,7 @@ class boss_selin_fireheart : public CreatureScript for (Creature* crystal : Crystals) { if (crystal && crystal->IsAlive()) - crystal->Kill(crystal); + crystal->KillSelf(); } } @@ -218,7 +218,7 @@ class boss_selin_fireheart : public CreatureScript Creature* CrystalChosen = ObjectAccessor::GetCreature(*me, CrystalGUID); if (CrystalChosen && CrystalChosen->IsAlive()) - CrystalChosen->Kill(CrystalChosen); + CrystalChosen->KillSelf(); CrystalGUID.Clear(); diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp index 2714d65774f..ce8b094bb83 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp @@ -1218,7 +1218,7 @@ public: if (Creature* temp = ObjectAccessor::GetCreature(*me, uiLichKingGUID)) // Lich king disappears here { temp->AI()->Talk(EMOTE_LIGHT_OF_DAWN17); - temp->Kill(temp); + temp->KillSelf(); } JumpToNextStep(10000); break; @@ -1633,7 +1633,7 @@ public: if (temp->IsAlive()) { temp->SetVisible(false); - temp->Kill(temp); + temp->KillSelf(); } } }; diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp index a78ad740c97..bac4e450a03 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp @@ -359,8 +359,8 @@ public: { die = false; if (Unit* body = ObjectAccessor::GetUnit(*me, bodyGUID)) - body->Kill(body); - me->Kill(me); + body->KillSelf(); + me->KillSelf(); } else wait -= diff; } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp index ed9f7ccb6dd..c41b1d87d61 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp @@ -121,7 +121,7 @@ class instance_scarlet_monastery : public InstanceMapScript { Creature* add = instance->GetCreature(guid); if (add && add->IsAlive()) - add->Kill(add); + add->KillSelf(); } HorsemanAdds.clear(); HandleGameObject(PumpkinShrineGUID, false); diff --git a/src/server/scripts/EasternKingdoms/Stratholme/stratholme.cpp b/src/server/scripts/EasternKingdoms/Stratholme/stratholme.cpp index ba99df8cdd5..60dd27eed90 100644 --- a/src/server/scripts/EasternKingdoms/Stratholme/stratholme.cpp +++ b/src/server/scripts/EasternKingdoms/Stratholme/stratholme.cpp @@ -170,7 +170,7 @@ public: { if (Player* player = temp->ToPlayer()) player->KilledMonsterCredit(NPC_RESTLESS, me->GetGUID()); - me->Kill(me); + me->KillSelf(); } } else @@ -248,7 +248,7 @@ public: if (Tagged) { if (Die_Timer <= diff) - me->Kill(me); + me->KillSelf(); else Die_Timer -= diff; } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp index 31ca716e0fb..77e0e680f4a 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp @@ -698,7 +698,7 @@ public: if (KillTimer <= diff) { - me->Kill(me); + me->KillSelf(); KillTimer = 9999999; } else KillTimer -= diff; diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp index 4d544ccdeab..e3deac506b0 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp @@ -1112,7 +1112,7 @@ public: else if (me->IsWithinDistInMap(me->GetVictim(), 3)) // Explode if it's close enough to it's target { DoCastVictim(SPELL_FELFIRE_FISSION); - me->Kill(me); + me->KillSelf(); } } }; @@ -1172,7 +1172,7 @@ public: uiTimer = 5000; break; case 3: - me->Kill(me); + me->KillSelf(); me->RemoveCorpse(); break; } diff --git a/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp b/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp index 9cecbc31665..9af23f17dca 100644 --- a/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp @@ -277,145 +277,6 @@ public: }; /*###### -## npc_anchorite_truuen -######*/ - -enum Truuen -{ - NPC_GHOST_UTHER = 17233, - NPC_THEL_DANIS = 1854, - NPC_GHOUL = 1791, //ambush - - QUEST_TOMB_LIGHTBRINGER = 9446, - - SAY_WP_0 = 0, //Beware! We are attacked! - SAY_WP_1 = 1, //It must be the purity of the Mark of the Lightbringer that is drawing forth the Scourge to attack us. We must proceed with caution lest we be overwhelmed! - SAY_WP_2 = 2, //This land truly needs to be cleansed by the Light! Let us continue on to the tomb. It isn't far now... - SAY_WP_3 = 0, //Be welcome, friends! - SAY_WP_4 = 0, //Thank you for coming here in remembrance of me. Your efforts in recovering that symbol, while unnecessary, are certainly touching to an old man's heart. - SAY_WP_5 = 1, //Please, rise my friend. Keep the Blessing as a symbol of the strength of the Light and how heroes long gone might once again rise in each of us to inspire. - SAY_WP_6 = 2 //Thank you my friend for making this possible. This is a day that I shall never forget! I think I will stay a while. Please return to High Priestess MacDonnell at the camp. I know that she'll be keenly interested to know of what has transpired here. -}; - -class npc_anchorite_truuen : public CreatureScript -{ -public: - npc_anchorite_truuen() : CreatureScript("npc_anchorite_truuen") { } - - bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest) override - { - if (quest->GetQuestId() == QUEST_TOMB_LIGHTBRINGER) - { - npc_escortAI* pEscortAI = ENSURE_AI(npc_anchorite_truuen::npc_anchorite_truuenAI, creature->AI()); - pEscortAI->Start(true, true, player->GetGUID()); - } - return false; - } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_anchorite_truuenAI(creature); - } - - struct npc_anchorite_truuenAI : public npc_escortAI - { - npc_anchorite_truuenAI(Creature* creature) : npc_escortAI(creature) - { - Initialize(); - } - - void Initialize() - { - m_uiChatTimer = 7000; - } - - uint32 m_uiChatTimer; - - ObjectGuid UghostGUID; - - void Reset() override - { - Initialize(); - } - - void JustSummoned(Creature* summoned) override - { - if (summoned->GetEntry() == NPC_GHOUL) - summoned->AI()->AttackStart(me); - } - - void WaypointReached(uint32 waypointId) override - { - Player* player = GetPlayerForEscort(); - - switch (waypointId) - { - case 8: - Talk(SAY_WP_0); - me->SummonCreature(NPC_GHOUL, me->GetPositionX()+7.0f, me->GetPositionY()+7.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000); - me->SummonCreature(NPC_GHOUL, me->GetPositionX()+5.0f, me->GetPositionY()+5.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000); - break; - case 9: - Talk(SAY_WP_1); - break; - case 14: - me->SummonCreature(NPC_GHOUL, me->GetPositionX()+7.0f, me->GetPositionY()+7.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000); - me->SummonCreature(NPC_GHOUL, me->GetPositionX()+5.0f, me->GetPositionY()+5.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000); - me->SummonCreature(NPC_GHOUL, me->GetPositionX()+10.0f, me->GetPositionY()+10.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000); - me->SummonCreature(NPC_GHOUL, me->GetPositionX()+8.0f, me->GetPositionY()+8.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000); - break; - case 15: - Talk(SAY_WP_2); - break; - case 21: - if (Creature* Theldanis = GetClosestCreatureWithEntry(me, NPC_THEL_DANIS, 150)) - Theldanis->AI()->Talk(SAY_WP_3); - break; - case 23: - if (Creature* Ughost = me->SummonCreature(NPC_GHOST_UTHER, 971.86f, -1825.42f, 81.99f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000)) - { - UghostGUID = Ughost->GetGUID(); - Ughost->SetDisableGravity(true); - Ughost->AI()->Talk(SAY_WP_4, me); - } - m_uiChatTimer = 4000; - break; - case 24: - if (Creature* Ughost = ObjectAccessor::GetCreature(*me, UghostGUID)) - Ughost->AI()->Talk(SAY_WP_5, me); - m_uiChatTimer = 4000; - break; - case 25: - if (Creature* Ughost = ObjectAccessor::GetCreature(*me, UghostGUID)) - Ughost->AI()->Talk(SAY_WP_6, me); - m_uiChatTimer = 4000; - break; - case 26: - if (player) - player->GroupEventHappens(QUEST_TOMB_LIGHTBRINGER, me); - break; - } - } - - void EnterCombat(Unit* /*who*/) override { } - - void JustDied(Unit* /*killer*/) override - { - if (Player* player = GetPlayerForEscort()) - player->FailQuest(QUEST_TOMB_LIGHTBRINGER); - } - - void UpdateAI(uint32 uiDiff) override - { - npc_escortAI::UpdateAI(uiDiff); - DoMeleeAttackIfReady(); - if (HasEscortState(STATE_ESCORT_ESCORTING)) - m_uiChatTimer = 6000; - } - }; -}; - -/*###### ## ######*/ @@ -425,5 +286,4 @@ void AddSC_western_plaguelands() new npc_myranda_the_hag(); new npc_the_scourge_cauldron(); new npc_andorhal_tower(); - new npc_anchorite_truuen(); } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp index 254e3a41d6b..d0a9b233454 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp @@ -235,7 +235,7 @@ public: if (AnetheronGUID) { Creature* boss = ObjectAccessor::GetCreature(*me, AnetheronGUID); - if (!boss || (boss && boss->isDead())) + if (!boss || boss->isDead()) { me->setDeathState(JUST_DIED); me->RemoveCorpse(); diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp index 08942e3393f..8bfdee067b2 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp @@ -200,7 +200,6 @@ public: CrippleTimer = 50000; WarstompTimer = 10000; CheckTimer = 5000; - instance = creature->GetInstanceScript(); AzgalorGUID = instance->GetGuidData(DATA_AZGALOR); } @@ -208,7 +207,6 @@ public: uint32 WarstompTimer; uint32 CheckTimer; ObjectGuid AzgalorGUID; - InstanceScript* instance; void Reset() override { @@ -248,7 +246,7 @@ public: if (AzgalorGUID) { Creature* boss = ObjectAccessor::GetCreature(*me, AzgalorGUID); - if (!boss || (boss && boss->isDead())) + if (!boss || boss->isDead()) { me->setDeathState(JUST_DIED); me->RemoveCorpse(); diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp index 70ecaad3d8d..c39fbc90795 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp @@ -218,7 +218,7 @@ public: break; case 5: me->SetVisible(false); - me->Kill(me); + me->KillSelf(); break; } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp index b5d2931377f..a73a6b8d998 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp @@ -817,7 +817,7 @@ public: cityman1->AI()->Talk(SAY_PHASE204); cityman1->SetTarget(me->GetGUID()); if (Creature* cityman0 = ObjectAccessor::GetCreature(*me, citymenGUID[0])) - cityman0->Kill(cityman0); + cityman0->KillSelf(); me->SetTarget(citymenGUID[1]); } JumpToNextStep(0); @@ -829,7 +829,7 @@ public: break; case 33: if (Creature* cityman1 = ObjectAccessor::GetCreature(*me, citymenGUID[1])) - cityman1->Kill(cityman1); + cityman1->KillSelf(); JumpToNextStep(1000); break; case 34: diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp index 8e84f1b4265..6016893a785 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp @@ -917,7 +917,7 @@ public: void JustDied(Unit* /*killer*/) override { if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) - p->Kill(p); + p->KillSelf(); } void Reset() override @@ -943,7 +943,7 @@ public: //KillSelfTimer if (KillSelfTimer <= diff) { - me->Kill(me); + me->KillSelf(); return; } else KillSelfTimer -= diff; @@ -997,7 +997,7 @@ public: void JustDied(Unit* /*killer*/) override { if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) - p->Kill(p); + p->KillSelf(); } void Reset() override @@ -1025,7 +1025,7 @@ public: if (EvadeTimer <= diff) { if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) - p->Kill(p); + p->KillSelf(); //Dissapear and reappear at new position me->SetVisible(false); @@ -1033,7 +1033,7 @@ public: Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0); if (!target) { - me->Kill(me); + me->KillSelf(); return; } @@ -1113,7 +1113,7 @@ public: void JustDied(Unit* /*killer*/) override { if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) - p->Kill(p); + p->KillSelf(); } void Reset() override @@ -1142,7 +1142,7 @@ public: if (EvadeTimer <= diff) { if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) - p->Kill(p); + p->KillSelf(); //Dissapear and reappear at new position me->SetVisible(false); @@ -1150,7 +1150,7 @@ public: Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0); if (!target) { - me->Kill(me); + me->KillSelf(); return; } @@ -1231,7 +1231,7 @@ public: void JustDied(Unit* /*killer*/) override { if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) - p->Kill(p); + p->KillSelf(); } void Reset() override diff --git a/src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp b/src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp index b0800918765..fba0956ccfc 100644 --- a/src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp +++ b/src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp @@ -352,7 +352,7 @@ class spell_ooze_zap_channel_end : public SpellScriptLoader PreventHitDefaultEffect(effIndex); if (Player* player = GetCaster()->ToPlayer()) player->CastSpell(player, SPELL_OOZE_CHANNEL_CREDIT, true); - GetHitUnit()->Kill(GetHitUnit()); + GetHitUnit()->KillSelf(); } void Register() override diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp index 163620230ef..c7decd3e286 100644 --- a/src/server/scripts/Kalimdor/zone_moonglade.cpp +++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp @@ -108,64 +108,6 @@ public: }; /*###### -## npc_great_bear_spirit -######*/ - -#define GOSSIP_BEAR1 "What do you represent, spirit?" -#define GOSSIP_BEAR2 "I seek to understand the importance of strength of the body." -#define GOSSIP_BEAR3 "I seek to understand the importance of strength of the heart." -#define GOSSIP_BEAR4 "I have heard your words, Great Bear Spirit, and I understand. I now seek your blessings to fully learn the way of the Claw." - -class npc_great_bear_spirit : public CreatureScript -{ -public: - npc_great_bear_spirit() : CreatureScript("npc_great_bear_spirit") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BEAR2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - player->SEND_GOSSIP_MENU(4721, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 1: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BEAR3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - player->SEND_GOSSIP_MENU(4733, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BEAR4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - player->SEND_GOSSIP_MENU(4734, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 3: - player->SEND_GOSSIP_MENU(4735, creature->GetGUID()); - if (player->GetQuestStatus(5929) == QUEST_STATUS_INCOMPLETE) - player->AreaExploredOrEventHappens(5929); - if (player->GetQuestStatus(5930) == QUEST_STATUS_INCOMPLETE) - player->AreaExploredOrEventHappens(5930); - break; - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - //ally or horde quest - if (player->GetQuestStatus(5929) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(5930) == QUEST_STATUS_INCOMPLETE) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BEAR1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - player->SEND_GOSSIP_MENU(4719, creature->GetGUID()); - } - else - player->SEND_GOSSIP_MENU(4718, creature->GetGUID()); - - return true; - } - -}; - -/*###### ## npc_silva_filnaveth ######*/ @@ -712,7 +654,6 @@ public: void AddSC_moonglade() { new npc_bunthen_plainswind(); - new npc_great_bear_spirit(); new npc_silva_filnaveth(); new npc_clintar_spirit(); new npc_omen(); diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp index 5890b9cbec7..240e8a7b957 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp @@ -433,7 +433,7 @@ public: { ENSURE_AI(boss_jedoga_shadowseeker::boss_jedoga_shadowseekerAI, boss->AI())->bOpFerok = true; ENSURE_AI(boss_jedoga_shadowseeker::boss_jedoga_shadowseekerAI, boss->AI())->bOpFerokFail = false; - me->Kill(me); + me->KillSelf(); } } break; diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp index eac84d1f6c9..3b12104a90b 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp @@ -338,14 +338,14 @@ struct dummy_dragonAI : public ScriptedAI if (instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) instance->SetBossState(DATA_SHADRON, DONE); if (Creature* acolyte = me->FindNearestCreature(NPC_ACOLYTE_OF_SHADRON, 100.0f)) - acolyte->Kill(acolyte); + acolyte->KillSelf(); break; case NPC_VESPERON: spellId = SPELL_POWER_OF_VESPERON; if (instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) instance->SetBossState(DATA_VESPERON, DONE); if (Creature* acolyte = me->FindNearestCreature(NPC_ACOLYTE_OF_VESPERON, 100.0f)) - acolyte->Kill(acolyte); + acolyte->KillSelf(); break; } @@ -948,7 +948,7 @@ public: //DoCastVictim(57620, true); //DoCastVictim(57874, true); me->RemoveAllAuras(); - me->Kill(me); + me->KillSelf(); } } diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp index 0b8fa7459a8..6bc49de5319 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp @@ -360,11 +360,11 @@ class boss_halion : public CreatureScript if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_TWILIGHT_HALION))) if (twilightHalion->IsAlive()) - twilightHalion->Kill(twilightHalion); + twilightHalion->KillSelf(); if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER))) if (controller->IsAlive()) - controller->Kill(controller); + controller->KillSelf(); } Position const* GetMeteorStrikePosition() const { return &_meteorStrikePos; } @@ -523,7 +523,7 @@ class boss_twilight_halion : public CreatureScript if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER))) if (controller->IsAlive()) - controller->Kill(controller); + controller->KillSelf(); instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp index b2aa6dadd84..78a279b94fd 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp @@ -831,7 +831,7 @@ class npc_vengeful_shade : public CreatureScript case SPELL_VENGEFUL_BLAST_25N: case SPELL_VENGEFUL_BLAST_10H: case SPELL_VENGEFUL_BLAST_25H: - me->Kill(me); + me->KillSelf(); break; default: break; @@ -1019,7 +1019,7 @@ class spell_cultist_dark_martyrdom : public SpellScriptLoader if (Unit* owner = GetCaster()->ToTempSummon()->GetSummoner()) owner->GetAI()->SetGUID(GetCaster()->GetGUID(), GUID_CULTIST); - GetCaster()->Kill(GetCaster()); + GetCaster()->KillSelf(); GetCaster()->SetDisplayId(uint32(GetCaster()->GetEntry() == NPC_CULT_FANATIC ? 38009 : 38010)); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp index 47569a6b85f..11fe5f41455 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp @@ -197,7 +197,7 @@ public: if (!guardCorpses.empty()) { if (ObjectGuid target = Trinity::Containers::SelectRandomContainerElement(guardCorpses)) - if(Creature* creatureTarget = ObjectAccessor::GetCreature(*me, target)) + if (Creature* creatureTarget = ObjectAccessor::GetCreature(*me, target)) { creatureTarget->CastSpell(creatureTarget, SPELL_SUMMON_CORPSE_SCARABS_MOB, true, nullptr, nullptr, me->GetGUID()); creatureTarget->AI()->Talk(EMOTE_SCARAB); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp index e52731d003e..0d938122f28 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp @@ -18,6 +18,7 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "PassiveAI.h" +#include "SpellScript.h" #include "naxxramas.h" enum Spells @@ -28,6 +29,14 @@ enum Spells SPELL_NECROTIC_POISON = 28776, SPELL_FRENZY = 54123 }; +#define SPELL_FRENZY_HELPER RAID_MODE(54123,54124) + +enum Emotes +{ + EMOTE_SPIDERS = 0, + EMOTE_WEB_WRAP = 1, + EMOTE_WEB_SPRAY = 2 +}; enum Creatures { @@ -35,12 +44,16 @@ enum Creatures NPC_SPIDERLING = 17055, }; -#define MAX_POS_WRAP 3 -const Position PosWrap[MAX_POS_WRAP] = +#define MAX_WRAP_POSITION 7 +const Position WrapPositions[MAX_WRAP_POSITION] = { - {3546.796f, -3869.082f, 296.450f, 0.0f}, - {3531.271f, -3847.424f, 299.450f, 0.0f}, - {3497.067f, -3843.384f, 302.384f, 0.0f}, + {3453.818f, -3854.651f, 308.7581f, 4.362833f}, + {3535.042f, -3842.383f, 300.795f, 3.179324f}, + {3538.399f, -3846.088f, 299.964f, 4.310297f}, + {3548.464f, -3854.676f, 298.6075f, 4.546609f}, + {3557.663f, -3870.123f, 297.5027f, 3.756433f}, + {3560.546f, -3879.353f, 297.4843f, 2.508937f}, + {3562.535f, -3892.507f, 298.532f, 6.022466f}, }; enum Events @@ -51,7 +64,24 @@ enum Events EVENT_POISON, EVENT_WRAP, EVENT_SUMMON, - EVENT_FRENZY, +}; + +const float WEB_WRAP_MOVE_SPEED = 20.0f; + +struct WebTargetSelector : public std::unary_function<Unit*, bool> +{ + WebTargetSelector(Unit* maexxna) : _maexxna(maexxna) {} + bool operator()(Unit const* target) const + { + if (_maexxna->GetVictim() == target) // never target tank + return false; + if (target->HasAura(SPELL_WEB_WRAP)) // never target targets that are already webbed + return false; + return true; + } + + private: + const Unit* _maexxna; }; class boss_maexxna : public CreatureScript @@ -66,27 +96,22 @@ public: struct boss_maexxnaAI : public BossAI { - boss_maexxnaAI(Creature* creature) : BossAI(creature, BOSS_MAEXXNA) - { - Initialize(); - } + boss_maexxnaAI(Creature* creature) : BossAI(creature, BOSS_MAEXXNA) { } - void Initialize() + void EnterCombat(Unit* /*who*/) override { - enraged = false; + _EnterCombat(); + events.ScheduleEvent(EVENT_WRAP, 20 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SPRAY, 40 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SHOCK, urandms(5, 10)); + events.ScheduleEvent(EVENT_POISON, urandms(10, 15)); + events.ScheduleEvent(EVENT_SUMMON, 30 * IN_MILLISECONDS); } - bool enraged; - - void EnterCombat(Unit* /*who*/) override + void Reset() override { - _EnterCombat(); - Initialize(); - events.ScheduleEvent(EVENT_WRAP, 20000); - events.ScheduleEvent(EVENT_SPRAY, 40000); - events.ScheduleEvent(EVENT_SHOCK, urand(5000, 10000)); - events.ScheduleEvent(EVENT_POISON, urand(10000, 15000)); - events.ScheduleEvent(EVENT_SUMMON, 30000); + _Reset(); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_WEB_WRAP); } void UpdateAI(uint32 diff) override @@ -94,10 +119,9 @@ public: if (!UpdateVictim() || !CheckInRoom()) return; - if (!enraged && HealthBelowPct(30)) + if (HealthBelowPct(30) && !me->HasAura(SPELL_FRENZY_HELPER)) { - enraged = true; - events.ScheduleEvent(EVENT_FRENZY, 0); // will be cast immediately + DoCast(SPELL_FRENZY); } events.Update(diff); @@ -107,41 +131,49 @@ public: switch (eventId) { case EVENT_WRAP: - /// @todo Add missing text - for (uint8 i = 0; i < RAID_MODE(1, 2); ++i) + { + std::list<Unit*> targets; + SelectTargetList(targets, WebTargetSelector(me), RAID_MODE(1, 2), SELECT_TARGET_RANDOM); + if (!targets.empty()) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0, true, -SPELL_WEB_WRAP)) + Talk(EMOTE_WEB_WRAP); + int8 wrapPos = -1; + for (Unit* target : targets) { + if (wrapPos == -1) // allow all positions on the first target + wrapPos = urand(0, MAX_WRAP_POSITION - 1); + else // on subsequent iterations, only allow positions that are not equal to the previous one (this is sufficient since we should only have two targets at most, ever) + wrapPos = (wrapPos + urand(1, MAX_WRAP_POSITION - 1)) % MAX_WRAP_POSITION; + target->RemoveAura(sSpellMgr->GetSpellIdForDifficulty(SPELL_WEB_SPRAY, me)); - uint8 pos = rand32() % MAX_POS_WRAP; - target->GetMotionMaster()->MoveJump(PosWrap[pos].GetPositionX(), PosWrap[pos].GetPositionY(), PosWrap[pos].GetPositionZ(), 20, 20); - if (Creature* wrap = DoSummon(NPC_WEB_WRAP, PosWrap[pos], 0, TEMPSUMMON_CORPSE_DESPAWN)) - wrap->AI()->SetGUID(target->GetGUID()); + if (Creature* wrap = DoSummon(NPC_WEB_WRAP, WrapPositions[wrapPos], 70 * IN_MILLISECONDS, TEMPSUMMON_TIMED_DESPAWN)) + { + wrap->AI()->SetGUID(target->GetGUID()); // handles application of debuff + target->GetMotionMaster()->MoveJump(WrapPositions[wrapPos], WEB_WRAP_MOVE_SPEED, WEB_WRAP_MOVE_SPEED); // move after stun to avoid stun cancelling move + } } } events.ScheduleEvent(EVENT_WRAP, 40000); break; + } case EVENT_SPRAY: + Talk(EMOTE_WEB_SPRAY); DoCastAOE(SPELL_WEB_SPRAY); events.ScheduleEvent(EVENT_SPRAY, 40000); break; case EVENT_SHOCK: DoCastAOE(SPELL_POISON_SHOCK); - events.ScheduleEvent(EVENT_SHOCK, urand(10000, 20000)); + events.ScheduleEvent(EVENT_SHOCK, urandms(10, 20)); break; case EVENT_POISON: DoCastVictim(SPELL_NECROTIC_POISON); - events.ScheduleEvent(EVENT_POISON, urand(10000, 20000)); - break; - case EVENT_FRENZY: - DoCast(me, SPELL_FRENZY, true); - events.ScheduleEvent(EVENT_FRENZY, 600000); + events.ScheduleEvent(EVENT_POISON, urandms(10, 20)); break; case EVENT_SUMMON: - /// @todo Add missing text + Talk(EMOTE_SPIDERS); uint8 amount = urand(8, 10); for (uint8 i = 0; i < amount; ++i) - DoSummon(NPC_SPIDERLING, me, 0, TEMPSUMMON_CORPSE_DESPAWN); + DoSummon(NPC_SPIDERLING, me, 4.0f, 5 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); events.ScheduleEvent(EVENT_SUMMON, 40000); break; } @@ -165,23 +197,49 @@ public: struct npc_webwrapAI : public NullCreatureAI { - npc_webwrapAI(Creature* creature) : NullCreatureAI(creature) { } + npc_webwrapAI(Creature* creature) : NullCreatureAI(creature), visibleTimer(0) { } ObjectGuid victimGUID; + uint32 visibleTimer; + + void InitializeAI() override + { + me->SetVisible(false); + } void SetGUID(ObjectGuid guid, int32 /*param*/) override { + if (!guid) + return; victimGUID = guid; - if (me->m_spells[0] && victimGUID) - if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID)) - victim->CastSpell(victim, me->m_spells[0], true, NULL, NULL, me->GetGUID()); + if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID)) + { + visibleTimer = (me->GetDistance2d(victim)/WEB_WRAP_MOVE_SPEED + 0.5f) * IN_MILLISECONDS; + victim->CastSpell(victim, SPELL_WEB_WRAP, true, NULL, NULL, me->GetGUID()); + } + } + + void UpdateAI(uint32 diff) override + { + if (!visibleTimer) + return; + + if (diff >= visibleTimer) + { + visibleTimer = 0; + me->SetVisible(true); + } + else + visibleTimer -= diff; } void JustDied(Unit* /*killer*/) override { - if (me->m_spells[0] && victimGUID) + if (victimGUID) if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID)) - victim->RemoveAurasDueToSpell(me->m_spells[0], me->GetGUID()); + victim->RemoveAurasDueToSpell(SPELL_WEB_WRAP, me->GetGUID()); + + me->DespawnOrUnsummon(5 * IN_MILLISECONDS); } }; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp index 106661b70bf..7b3a9f8ac74 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp @@ -155,7 +155,7 @@ public: void KilledUnit(Unit* victim) override { - if(victim->GetTypeId() == TYPEID_PLAYER) + if (victim->GetTypeId() == TYPEID_PLAYER) Talk(SAY_SLAY); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp index 1683667a02a..f94f7b227bf 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp @@ -17,43 +17,40 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellInfo.h" #include "naxxramas.h" -//Razuvious - NO TEXT sound only -//8852 aggro01 - Hah hah, I'm just getting warmed up! -//8853 aggro02 Stand and fight! -//8854 aggro03 Show me what you've got! -//8861 slay1 - You should've stayed home! -//8863 slay2- -//8858 cmmnd3 - You disappoint me, students! -//8855 cmmnd1 - Do as I taught you! -//8856 cmmnd2 - Show them no mercy! -//8859 cmmnd4 - The time for practice is over! Show me what you've learned! -//8861 Sweep the leg! Do you have a problem with that? -//8860 death - An honorable... death... -//8947 - Aggro Mixed? - ? - -#define SOUND_AGGRO RAND(8852, 8853, 8854) -#define SOUND_SLAY RAND(8861, 8863) -#define SOUND_COMMND RAND(8855, 8856, 8858, 8859, 8861) -#define SOUND_DEATH 8860 -#define SOUND_AGGROMIX 8847 +enum Yells +{ + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_TAUNTED = 2, + SAY_DEATH = 3 +}; enum Spells { - SPELL_UNBALANCING_STRIKE = 26613, - SPELL_DISRUPTING_SHOUT = 29107, - SPELL_JAGGED_KNIFE = 55550, - SPELL_HOPELESS = 29125 + SPELL_UNBALANCING_STRIKE = 26613, + SPELL_DISRUPTING_SHOUT = 29107, + SPELL_JAGGED_KNIFE = 55550, + SPELL_HOPELESS = 29125, + SPELL_UNDERSTUDY_TAUNT = 29060, + SPELL_UNDERSTUDY_BLOOD_STRIKE = 61696, + SPELL_FORCE_OBEDIENCE = 55479 }; enum Events { - EVENT_NONE, + EVENT_ATTACK = 1, EVENT_STRIKE, EVENT_SHOUT, - EVENT_KNIFE, - EVENT_COMMAND, + EVENT_KNIFE +}; + +enum SummonGroups +{ + SUMMON_GROUP_10MAN = 1, + SUMMON_GROUP_25MAN = 2 }; class boss_razuvious : public CreatureScript @@ -70,36 +67,60 @@ public: { boss_razuviousAI(Creature* creature) : BossAI(creature, BOSS_RAZUVIOUS) { } - void KilledUnit(Unit* /*victim*/) override + void SummonAdds() { - if (!(rand32() % 3)) - DoPlaySoundToSet(me, SOUND_SLAY); + me->SummonCreatureGroup(SUMMON_GROUP_10MAN); + if (Is25ManRaid()) + me->SummonCreatureGroup(SUMMON_GROUP_25MAN); } - void DamageTaken(Unit* pDone_by, uint32& uiDamage) override + void InitializeAI() override { - // Damage done by the controlled Death Knight understudies should also count toward damage done by players - if (pDone_by->GetTypeId() == TYPEID_UNIT && (pDone_by->GetEntry() == 16803 || pDone_by->GetEntry() == 29941)) + if (!me->isDead()) { - me->LowerPlayerDamageReq(uiDamage); + Reset(); + SummonAdds(); } } + void JustReachedHome() override + { + _JustReachedHome(); + SummonAdds(); + me->GetMotionMaster()->Initialize(); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER || (victim->GetTypeId() == TYPEID_UNIT && victim->GetEntry() == NPC_DK_UNDERSTUDY)) + Talk(SAY_SLAY); + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_UNDERSTUDY_TAUNT) + Talk(SAY_TAUNTED, caster); + } + void JustDied(Unit* /*killer*/) override { - _JustDied(); - DoPlaySoundToSet(me, SOUND_DEATH); - me->CastSpell(me, SPELL_HOPELESS, true); /// @todo this may affect other creatures + Talk(SAY_DEATH); + DoCastAOE(SPELL_HOPELESS, true); + + events.Reset(); + instance->SetBossState(BOSS_RAZUVIOUS, DONE); } void EnterCombat(Unit* /*who*/) override { _EnterCombat(); - DoPlaySoundToSet(me, SOUND_AGGRO); - events.ScheduleEvent(EVENT_STRIKE, 30000); - events.ScheduleEvent(EVENT_SHOUT, 25000); - events.ScheduleEvent(EVENT_COMMAND, 40000); - events.ScheduleEvent(EVENT_KNIFE, 10000); + me->StopMoving(); + summons.DoZoneInCombat(); + Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_ATTACK, 7 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_STRIKE, 21 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SHOUT, 16 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_KNIFE, 10 * IN_MILLISECONDS); } void UpdateAI(uint32 diff) override @@ -113,33 +134,112 @@ public: { switch (eventId) { + case EVENT_ATTACK: + SetCombatMovement(true); + if (Unit* victim = me->GetVictim()) + me->GetMotionMaster()->MoveChase(victim); + break; case EVENT_STRIKE: DoCastVictim(SPELL_UNBALANCING_STRIKE); - events.ScheduleEvent(EVENT_STRIKE, 30000); + events.ScheduleEvent(EVENT_STRIKE, 6 * IN_MILLISECONDS); return; case EVENT_SHOUT: DoCastAOE(SPELL_DISRUPTING_SHOUT); - events.ScheduleEvent(EVENT_SHOUT, 25000); + events.ScheduleEvent(EVENT_SHOUT, 16 * IN_MILLISECONDS); return; case EVENT_KNIFE: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f)) DoCast(target, SPELL_JAGGED_KNIFE); - events.ScheduleEvent(EVENT_KNIFE, 10000); - return; - case EVENT_COMMAND: - DoPlaySoundToSet(me, SOUND_COMMND); - events.ScheduleEvent(EVENT_COMMAND, 40000); + events.ScheduleEvent(EVENT_KNIFE, urandms(10,15)); return; } } DoMeleeAttackIfReady(); } + + void Reset() override + { + SetCombatMovement(false); + _Reset(); + } }; }; +class npc_dk_understudy : public CreatureScript +{ + public: + npc_dk_understudy() : CreatureScript("npc_dk_understudy") { } + + struct npc_dk_understudyAI : public ScriptedAI + { + npc_dk_understudyAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), bloodStrikeTimer(0) { } + + void EnterCombat(Unit* /*who*/) override + { + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + if (Creature* razuvious = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_RAZUVIOUS))) + razuvious->AI()->DoZoneInCombat(nullptr, 250.0f); + } + + void JustReachedHome() override + { + if (_instance->GetBossState(BOSS_RAZUVIOUS) == DONE) + me->DespawnOrUnsummon(); + else + ScriptedAI::JustReachedHome(); + } + + void UpdateAI(uint32 diff) override + { + if (!me->isPossessedByPlayer() && !UpdateVictim()) + return; + + if (!me->isPossessedByPlayer()) + { + if (diff < bloodStrikeTimer) + bloodStrikeTimer -= diff; + else + DoCastVictim(SPELL_UNDERSTUDY_BLOOD_STRIKE); + } + + DoMeleeAttackIfReady(); + } + + void OnCharmed(bool apply) override + { + ScriptedAI::OnCharmed(apply); + if (apply) + { + if (!me->IsInCombat()) + EnterCombat(nullptr); + me->StopMoving(); + me->SetReactState(REACT_PASSIVE); + _charmer = me->GetCharmerGUID(); + } + else + { + me->SetReactState(REACT_AGGRESSIVE); + if (Unit* charmer = ObjectAccessor::GetUnit(*me, _charmer)) + me->AddThreat(charmer, 100000.0f); + DoZoneInCombat(nullptr, 250.0f); + } + } + private: + InstanceScript* const _instance; + ObjectGuid _charmer; + uint32 bloodStrikeTimer; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_dk_understudyAI>(creature); + } +}; + void AddSC_boss_razuvious() { new boss_razuvious(); + new npc_dk_understudy(); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp index d74fd5a03f8..5d3b038d41d 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp @@ -19,48 +19,104 @@ #include "ScriptedCreature.h" #include "SpellScript.h" #include "Player.h" +#include "ObjectGuid.h" #include "naxxramas.h" -//Stalagg -enum StalaggYells + +enum Phases +{ + PHASE_NOT_ENGAGED = 1, + PHASE_PETS, + PHASE_TRANSITION, + PHASE_THADDIUS, + PHASE_RESETTING +}; + +enum AIActions { - SAY_STAL_AGGRO = 0, - SAY_STAL_SLAY = 1, - SAY_STAL_DEATH = 2 + ACTION_RESET_ENCOUNTER_TIMER = -1, // sent from instance AI + ACTION_BEGIN_RESET_ENCOUNTER = 0, // sent from thaddius to pets to trigger despawn and encounter reset + ACTION_RESET_ENCOUNTER, // sent from thaddius to pets to trigger respawn and full reset + ACTION_FEUGEN_DIED, // sent from respective pet to thaddius to indicate death + ACTION_STALAGG_DIED, // ^ + ACTION_FEUGEN_RESET, // pet to thaddius + ACTION_STALAGG_RESET, // ^ + ACTION_FEUGEN_AGGRO, // pet to thaddius on combat start + ACTION_STALAGG_AGGRO, // ^ + ACTION_FEUGEN_REVIVING_FX, // thaddius to pet when pet reports its death + ACTION_STALAGG_REVIVING_FX, // ^ + ACTION_FEUGEN_REVIVED, // thaddius to pet when pet should revive + ACTION_STALAGG_REVIVED, // ^ + ACTION_TRANSITION, // thaddius to pets when transition starts (coil overload anim) + ACTION_TRANSITION_2, // thaddius to pets to make the coils shock him + ACTION_TRANSITION_3, // thaddius to pets to disable coil GO after spawn + + ACTION_POLARITY_CROSSED // triggers achievement failure, sent from spellscript }; -enum StalagSpells +enum Events { - SPELL_POWERSURGE = 28134, - SPELL_MAGNETIC_PULL = 28338, - SPELL_STALAGG_TESLA = 28097 + EVENT_SHIFT = 1, // polarity shift + EVENT_SHIFT_TALK, // polarity shift yell (hack? couldn't find any event for cast finish) + EVENT_CHAIN, // chain lightning + EVENT_BERSERK, // enrage timer + EVENT_REVIVE_FEUGEN, // timer until feugen is revived (if stalagg still lives) + EVENT_REVIVE_STALAGG, // timer until stalagg is revived (if feugen still lives) + EVENT_TRANSITION_1, // timer until overload emote + EVENT_TRANSITION_2, // timer until thaddius gets zapped by the coils + EVENT_TRANSITION_3, // timer until thaddius engages + EVENT_ENABLE_BALL_LIGHTNING // grace period after thaddius aggro after which he starts being a baller (e.g. tossing ball lightning at out of range targets) }; -//Feugen -enum FeugenYells +enum Misc { - SAY_FEUG_AGGRO = 0, - SAY_FEUG_SLAY = 1, - SAY_FEUG_DEATH = 2 + MAX_POLARITY_10M = 5, + MAX_POLARITY_25M = 13, + + DATA_POLARITY_CROSSED = 1, +}; + +// Feugen & Stalagg +enum PetYells +{ + SAY_STALAGG_AGGRO = 0, + SAY_STALAGG_SLAY = 1, + SAY_STALAGG_DEATH = 2, + + SAY_FEUGEN_AGGRO = 0, + SAY_FEUGEN_SLAY = 1, + SAY_FEUGEN_DEATH = 2, + + EMOTE_FEIGN_DEATH = 3, + EMOTE_FEIGN_REVIVE = 4, + + EMOTE_TESLA_LINK_BREAKS = 0, + EMOTE_TESLA_OVERLOAD = 1 }; -enum FeugenSpells +enum PetSpells { - SPELL_STATICFIELD = 28135, - SPELL_FEUGEN_TESLA = 28109 + SPELL_STALAGG_POWERSURGE = 28134, + //SPELL_STALAGG_TESLA = 28097, + SPELL_STALAGG_TESLA_PERIODIC = 28098, + SPELL_STALAGG_CHAIN_VISUAL = 28096, + + SPELL_FEUGEN_STATICFIELD = 28135, + //SPELL_FEUGEN_TESLA = 28109, + SPELL_FEUGEN_TESLA_PERIODIC = 28110, + SPELL_FEUGEN_CHAIN_VISUAL = 28111, + + SPELL_MAGNETIC_PULL = 54517, + SPELL_MAGNETIC_PULL_EFFECT = 28337, + + SPELL_TESLA_SHOCK = 28099 }; -// Thaddius DoAction -enum ThaddiusActions +enum PetMisc { - ACTION_FEUGEN_RESET, - ACTION_FEUGEN_DIED, - ACTION_STALAGG_RESET, - ACTION_STALAGG_DIED + OVERLOAD_DISTANCE = 28 }; -//generic -#define C_TESLA_COIL 16218 //the coils (emotes "Tesla Coil overloads!") //Thaddius enum ThaddiusYells @@ -70,34 +126,31 @@ enum ThaddiusYells SAY_SLAY = 2, SAY_ELECT = 3, SAY_DEATH = 4, - SAY_SCREAM = 5 + SAY_SCREAM = 5, + + EMOTE_POLARITY_SHIFTED = 6 }; enum ThaddiusSpells { - SPELL_POLARITY_SHIFT = 28089, - SPELL_BALL_LIGHTNING = 28299, - SPELL_CHAIN_LIGHTNING = 28167, - SPELL_BERSERK = 27680, - SPELL_POSITIVE_CHARGE = 28062, - SPELL_POSITIVE_CHARGE_STACK = 29659, - SPELL_NEGATIVE_CHARGE = 28085, - SPELL_NEGATIVE_CHARGE_STACK = 29660, - SPELL_POSITIVE_POLARITY = 28059, - SPELL_NEGATIVE_POLARITY = 28084, -}; + SPELL_THADDIUS_INACTIVE_VISUAL = 28160, + SPELL_THADDIUS_SPARK_VISUAL = 28136, + SPELL_SHOCK_VISUAL = 28159, -enum Events -{ - EVENT_NONE, - EVENT_SHIFT, - EVENT_CHAIN, - EVENT_BERSERK, -}; + SPELL_BALL_LIGHTNING = 28299, + SPELL_CHAIN_LIGHTNING = 28167, + SPELL_BERSERK = 27680, -enum Achievement -{ - DATA_POLARITY_SWITCH = 76047605, + // polarity handling + SPELL_POLARITY_SHIFT = 28089, + + SPELL_POSITIVE_CHARGE_APPLY = 28059, + SPELL_POSITIVE_CHARGE_TICK = 28062, + SPELL_POSITIVE_CHARGE_AMP = 29659, + + SPELL_NEGATIVE_CHARGE_APPLY = 28084, + SPELL_NEGATIVE_CHARGE_TICK = 28085, + SPELL_NEGATIVE_CHARGE_AMP = 29660, }; class boss_thaddius : public CreatureScript @@ -112,166 +165,276 @@ public: struct boss_thaddiusAI : public BossAI { - boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS) - { - // init is a bit tricky because thaddius shall track the life of both adds, but not if there was a wipe - // and, in particular, if there was a crash after both adds were killed (should not respawn) - - // Moreover, the adds may not yet be spawn. So just track down the status if mob is spawn - // and each mob will send its status at reset (meaning that it is alive) - checkFeugenAlive = false; - if (Creature* pFeugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - checkFeugenAlive = pFeugen->IsAlive(); + public: + boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningEnabled(false), shockingEligibility(true) + { + if (instance->GetBossState(BOSS_THADDIUS) != DONE) + { + events.SetPhase(PHASE_NOT_ENGAGED); + SetCombatMovement(false); - checkStalaggAlive = false; - if (Creature* pStalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - checkStalaggAlive = pStalagg->IsAlive(); + // initialize everything properly, and ensure that the coils are loaded by the time we initialize + BeginResetEncounter(true); + } + } - if (!checkFeugenAlive && !checkStalaggAlive) + void KilledUnit(Unit* victim) override { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); - me->SetReactState(REACT_AGGRESSIVE); + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); } - else + + void Reset() override { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); - me->SetReactState(REACT_PASSIVE); + if (events.IsInPhase(PHASE_TRANSITION) || (events.IsInPhase(PHASE_THADDIUS) && me->IsAlive())) + BeginResetEncounter(); } - polaritySwitch = false; - uiAddsTimer = 0; - } - - bool checkStalaggAlive; - bool checkFeugenAlive; - bool polaritySwitch; - uint32 uiAddsTimer; - - void KilledUnit(Unit* /*victim*/) override - { - if (!(rand32() % 5)) - Talk(SAY_SLAY); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - Talk(SAY_DEATH); - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + me->setActive(false); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->setActive(false); + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->setActive(false); + Talk(SAY_DEATH); + } - void DoAction(int32 action) override - { - switch (action) + void DoAction(int32 action) override { - case ACTION_FEUGEN_RESET: - checkFeugenAlive = true; - break; - case ACTION_FEUGEN_DIED: - checkFeugenAlive = false; - break; - case ACTION_STALAGG_RESET: - checkStalaggAlive = true; - break; - case ACTION_STALAGG_DIED: - checkStalaggAlive = false; - break; + switch (action) + { + case ACTION_RESET_ENCOUNTER_TIMER: + if (events.IsInPhase(PHASE_RESETTING)) + ResetEncounter(); + break; + case ACTION_FEUGEN_RESET: + case ACTION_STALAGG_RESET: + if (!events.IsInPhase(PHASE_NOT_ENGAGED) && !events.IsInPhase(PHASE_RESETTING)) + BeginResetEncounter(); + break; + case ACTION_FEUGEN_AGGRO: + case ACTION_STALAGG_AGGRO: + if (events.IsInPhase(PHASE_RESETTING)) + return BeginResetEncounter(); + if (!events.IsInPhase(PHASE_NOT_ENGAGED)) + return; + events.SetPhase(PHASE_PETS); + + shockingEligibility = true; + + if (!instance->CheckRequiredBosses(BOSS_THADDIUS)) + return BeginResetEncounter(); + instance->SetBossState(BOSS_THADDIUS, IN_PROGRESS); + + me->setActive(true); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->setActive(true); + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->setActive(true); + break; + case ACTION_FEUGEN_DIED: + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_FEUGEN_REVIVING_FX); + feugenAlive = false; + if (stalaggAlive) + events.ScheduleEvent(EVENT_REVIVE_FEUGEN, 5 * IN_MILLISECONDS, 0, PHASE_PETS); + else + Transition(); + + break; + case ACTION_STALAGG_DIED: + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_STALAGG_REVIVING_FX); + stalaggAlive = false; + if (feugenAlive) + events.ScheduleEvent(EVENT_REVIVE_STALAGG, 5 * IN_MILLISECONDS, 0, PHASE_PETS); + else + Transition(); + + break; + + case ACTION_POLARITY_CROSSED: + shockingEligibility = false; + break; + default: + break; + } } - if (!checkFeugenAlive && !checkStalaggAlive) + uint32 GetData(uint32 id) const override { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); - // REACT_AGGRESSIVE only reset when he takes damage. - DoZoneInCombat(); + return (id == DATA_POLARITY_CROSSED && shockingEligibility) ? 1u : 0u; } - else + + void Transition() // initiate transition between pet phase and thaddius phase { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); - me->SetReactState(REACT_PASSIVE); + events.SetPhase(PHASE_TRANSITION); + + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + events.ScheduleEvent(EVENT_TRANSITION_1, 10 * IN_MILLISECONDS, 0, PHASE_TRANSITION); + events.ScheduleEvent(EVENT_TRANSITION_2, 12 * IN_MILLISECONDS, 0, PHASE_TRANSITION); + events.ScheduleEvent(EVENT_TRANSITION_3, 14 * IN_MILLISECONDS, 0, PHASE_TRANSITION); } - } - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_SHIFT, 30000); - events.ScheduleEvent(EVENT_CHAIN, urand(10000, 20000)); - events.ScheduleEvent(EVENT_BERSERK, 360000); - } + void BeginResetEncounter(bool initial = false) + { + if (instance->GetBossState(BOSS_THADDIUS) == DONE) + return; + if (events.IsInPhase(PHASE_RESETTING)) + return; - void DamageTaken(Unit* /*pDoneBy*/, uint32 & /*uiDamage*/) override - { - me->SetReactState(REACT_AGGRESSIVE); - } + if (initial) // signal shorter spawn timer to instance script + instance->SetBossState(BOSS_THADDIUS, SPECIAL); + instance->ProcessEvent(me, EVENT_THADDIUS_BEGIN_RESET); + instance->SetBossState(BOSS_THADDIUS, NOT_STARTED); - void SetData(uint32 id, uint32 data) override - { - if (id == DATA_POLARITY_SWITCH) - polaritySwitch = data ? true : false; - } + // remove polarity shift debuffs on reset + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_APPLY); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_APPLY); - uint32 GetData(uint32 id) const override - { - if (id != DATA_POLARITY_SWITCH) - return 0; + me->DespawnOrUnsummon(); - return uint32(polaritySwitch); - } + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); + events.SetPhase(PHASE_RESETTING); + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER); - void UpdateAI(uint32 diff) override - { - if (checkFeugenAlive && checkStalaggAlive) - uiAddsTimer = 0; + me->setActive(false); + } - if (checkStalaggAlive != checkFeugenAlive) + void ResetEncounter() { - uiAddsTimer += diff; - if (uiAddsTimer > 5000) - { - if (!checkStalaggAlive) - { - if (Creature* pStalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - pStalagg->Respawn(); - } - else - { - if (Creature* pFeugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - pFeugen->Respawn(); - } - } - } + feugenAlive = true; + stalaggAlive = true; - if (!UpdateVictim()) - return; + me->Respawn(true); + _Reset(); + events.SetPhase(PHASE_NOT_ENGAGED); - events.Update(diff); + me->CastSpell(me, SPELL_THADDIUS_INACTIVE_VISUAL, true); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_RESET_ENCOUNTER); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_RESET_ENCOUNTER); + } - while (uint32 eventId = events.ExecuteEvent()) + void UpdateAI(uint32 diff) override { - switch (eventId) + if (events.IsInPhase(PHASE_NOT_ENGAGED)) + return; + if (events.IsInPhase(PHASE_THADDIUS) && !UpdateVictim()) + return; + + events.Update(diff); + while (uint32 eventId = events.ExecuteEvent()) { - case EVENT_SHIFT: - DoCastAOE(SPELL_POLARITY_SHIFT); - events.ScheduleEvent(EVENT_SHIFT, 30000); - return; - case EVENT_CHAIN: - DoCastVictim(SPELL_CHAIN_LIGHTNING); - events.ScheduleEvent(EVENT_CHAIN, urand(10000, 20000)); - return; - case EVENT_BERSERK: - DoCast(me, SPELL_BERSERK); - return; + switch (eventId) + { + case EVENT_REVIVE_FEUGEN: + feugenAlive = true; + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_FEUGEN_REVIVED); + break; + case EVENT_REVIVE_STALAGG: + stalaggAlive = true; + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_STALAGG_REVIVED); + break; + case EVENT_TRANSITION_1: // tesla coils overload + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_TRANSITION); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_TRANSITION); + break; + case EVENT_TRANSITION_2: // tesla coils shock thaddius + me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true); + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_TRANSITION_2); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_TRANSITION_2); + break; + case EVENT_TRANSITION_3: // thaddius becomes active + me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true); + ballLightningEnabled = false; + me->RemoveAura(SPELL_THADDIUS_INACTIVE_VISUAL); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + + DoZoneInCombat(); + if (Unit* closest = SelectTarget(SELECT_TARGET_NEAREST, 0, 500.0f)) + AttackStart(closest); + else // if there is no nearest target, then there is no target, meaning we should reset + return BeginResetEncounter(); + + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_TRANSITION_3); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_TRANSITION_3); + + events.SetPhase(PHASE_THADDIUS); + + Talk(SAY_AGGRO); + + events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, 5 * IN_MILLISECONDS, 0, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_SHIFT, 10 * IN_MILLISECONDS, 0, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_CHAIN, urand(10, 20) * IN_MILLISECONDS, 0, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_BERSERK, 6 * MINUTE * IN_MILLISECONDS, 0, PHASE_THADDIUS); + + break; + case EVENT_ENABLE_BALL_LIGHTNING: + ballLightningEnabled = true; + break; + case EVENT_SHIFT: + me->CastStop(); // shift overrides all other spells + DoCastAOE(SPELL_POLARITY_SHIFT); + events.ScheduleEvent(EVENT_SHIFT_TALK, 3 * IN_MILLISECONDS, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_SHIFT, 30 * IN_MILLISECONDS, PHASE_THADDIUS); + break; + case EVENT_SHIFT_TALK: + Talk(SAY_ELECT); + Talk(EMOTE_POLARITY_SHIFTED); + break; + case EVENT_CHAIN: + if (me->FindCurrentSpellBySpellId(SPELL_POLARITY_SHIFT)) // delay until shift is over + events.ScheduleEvent(EVENT_CHAIN, 3 * IN_MILLISECONDS, 0, PHASE_THADDIUS); + else + { + me->CastStop(); + DoCastVictim(SPELL_CHAIN_LIGHTNING); + events.ScheduleEvent(EVENT_CHAIN, urand(10, 20) * IN_MILLISECONDS, PHASE_THADDIUS); + } + break; + case EVENT_BERSERK: + me->CastStop(); + DoCast(me, SPELL_BERSERK); + break; + default: + break; + } + } + + if (events.IsInPhase(PHASE_THADDIUS)) + { + if (me->IsWithinMeleeRange(me->GetVictim())) + DoMeleeAttackIfReady(); + else + if (ballLightningEnabled) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) + DoCast(target, SPELL_BALL_LIGHTNING); } } - if (events.GetTimer() > 15000 && !me->IsWithinMeleeRange(me->GetVictim())) - DoCastVictim(SPELL_BALL_LIGHTNING); - else - DoMeleeAttackIfReady(); - } + private: + bool stalaggAlive; + bool feugenAlive; + bool ballLightningEnabled; + bool shockingEligibility; }; }; @@ -288,88 +451,257 @@ public: struct npc_stalaggAI : public ScriptedAI { - npc_stalaggAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } + public: + npc_stalaggAI(Creature* creature) : ScriptedAI(creature), _myCoil(ObjectGuid::Empty), _myCoilGO(ObjectGuid::Empty), isOverloading(false), refreshBeam(false), isFeignDeath(false) + { + Initialize(); + instance = creature->GetInstanceScript(); + } - void Initialize() - { - powerSurgeTimer = urand(20000, 25000); - magneticPullTimer = 20000; - } + void Initialize() + { + if (GameObject* coil = myCoilGO()) + coil->SetGoState(GO_STATE_ACTIVE); - InstanceScript* instance; + // if the encounter reset while feigning death + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + isOverloading = false; + isFeignDeath = false; - uint32 powerSurgeTimer; - uint32 magneticPullTimer; + // force tesla coil state refresh + refreshBeam = true; - void Reset() override - { - if (Creature* pThaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) - if (pThaddius->AI()) - pThaddius->AI()->DoAction(ACTION_STALAGG_RESET); - Initialize(); - } + powerSurgeTimer = 10 * IN_MILLISECONDS; + } - void KilledUnit(Unit* /*victim*/) override - { - if (!(rand32() % 5)) - Talk(SAY_STAL_SLAY); - } + void Reset() override + { + if (isFeignDeath || !me->IsAlive()) + return; + if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) + thaddius->AI()->DoAction(ACTION_STALAGG_RESET); + } - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_STAL_AGGRO); - DoCast(SPELL_STALAGG_TESLA); - } + void BeginResetEncounter() + { + if (GameObject* coil = myCoilGO()) + coil->SetGoState(GO_STATE_READY); + me->DespawnOrUnsummon(); + me->setActive(false); + } - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_STAL_DEATH); - if (Creature* pThaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) - if (pThaddius->AI()) - pThaddius->AI()->DoAction(ACTION_STALAGG_DIED); - } + void ResetEncounter() + { + me->Respawn(true); + Initialize(); + } - void UpdateAI(uint32 uiDiff) override - { - if (!UpdateVictim()) - return; + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_BEGIN_RESET_ENCOUNTER: + BeginResetEncounter(); + break; + case ACTION_RESET_ENCOUNTER: + ResetEncounter(); + break; + case ACTION_STALAGG_REVIVING_FX: + break; + case ACTION_STALAGG_REVIVED: + if (!isFeignDeath) + break; + + me->SetFullHealth(); + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Talk(EMOTE_FEIGN_REVIVE); + isFeignDeath = false; + + refreshBeam = true; // force beam refresh + + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + if (feugen->GetVictim()) + { + me->AddThreat(feugen->EnsureVictim(), 0.0f); + me->SetInCombatWith(feugen->EnsureVictim()); + } + break; + case ACTION_TRANSITION: + me->KillSelf(); // true death + me->DespawnOrUnsummon(); + + if (Creature* coil = myCoil()) + { + coil->CastStop(); + coil->AI()->Talk(EMOTE_TESLA_OVERLOAD); + } + break; + case ACTION_TRANSITION_2: + if (Creature* coil = myCoil()) + if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) + coil->CastSpell(thaddius, SPELL_SHOCK_VISUAL); + break; + case ACTION_TRANSITION_3: + if (GameObject* coil = myCoilGO()) + coil->SetGoState(GO_STATE_READY); + break; + default: + break; + } + } - if (magneticPullTimer <= uiDiff) + void KilledUnit(Unit* victim) override { - if (Creature* pFeugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_STALAGG_SLAY); + } + + void EnterCombat(Unit* who) override + { + Talk(SAY_STALAGG_AGGRO); + + if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) + thaddius->AI()->DoAction(ACTION_STALAGG_AGGRO); + + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + if (!feugen->IsInCombat()) + { + feugen->AddThreat(who, 0.0f); + feugen->SetInCombatWith(who); + } + } + + void DamageTaken(Unit* /*who*/, uint32& damage) override + { + if (damage < me->GetHealth()) + return; + + if (isFeignDeath) // don't take damage while feigning death { - Unit* pStalaggVictim = me->GetVictim(); - Unit* pFeugenVictim = pFeugen->GetVictim(); + damage = 0; + return; + } + + isFeignDeath = true; + isOverloading = false; + + Talk(EMOTE_FEIGN_DEATH); + if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) + thaddius->AI()->DoAction(ACTION_STALAGG_DIED); + + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAllAuras(); + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->StopMoving(); + me->SetStandState(UNIT_STAND_STATE_DEAD); - if (pFeugenVictim && pStalaggVictim) + damage = 0; + + // force beam refresh as we just removed auras + refreshBeam = true; + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (!caster) + return; + if (spell->Id != SPELL_STALAGG_TESLA_PERIODIC) + return; + if (!isFeignDeath && me->IsInCombat() && !me->GetHomePosition().IsInDist(me, OVERLOAD_DISTANCE)) + { + if (!isOverloading) { - // magnetic pull is not working. So just jump. + isOverloading = true; + caster->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + if (Creature* creatureCaster = caster->ToCreature()) + creatureCaster->AI()->Talk(EMOTE_TESLA_LINK_BREAKS); + me->RemoveAura(SPELL_STALAGG_CHAIN_VISUAL); + } + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) + { + caster->CastStop(SPELL_TESLA_SHOCK); + caster->CastSpell(target, SPELL_TESLA_SHOCK,true); + } + } + else if (isOverloading || refreshBeam) + { + isOverloading = false; + refreshBeam = false; + caster->CastStop(); + caster->CastSpell(me, SPELL_STALAGG_CHAIN_VISUAL, true); + caster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + } + } - // reset aggro to be sure that feugen will not follow the jump - pFeugen->getThreatManager().modifyThreatPercent(pFeugenVictim, -100); - pFeugenVictim->JumpTo(me, 0.3f); + void UpdateAI(uint32 uiDiff) override + { + if (!isFeignDeath) + if (!UpdateVictim()) + return; - me->getThreatManager().modifyThreatPercent(pStalaggVictim, -100); - pStalaggVictim->JumpTo(pFeugen, 0.3f); + if (powerSurgeTimer <= uiDiff) + { + if (isFeignDeath) // delay until potential revive + powerSurgeTimer = 0u; + else + { + DoCast(me, SPELL_STALAGG_POWERSURGE); + powerSurgeTimer = urand(25, 30) * IN_MILLISECONDS; } } + else + powerSurgeTimer -= uiDiff; - magneticPullTimer = 20000; + if (!isFeignDeath) + DoMeleeAttackIfReady(); } - else magneticPullTimer -= uiDiff; - if (powerSurgeTimer <= uiDiff) + private: + Creature* myCoil() { - DoCast(me, SPELL_POWERSURGE); - powerSurgeTimer = urand(15000, 20000); - } else powerSurgeTimer -= uiDiff; + Creature* coil = nullptr; + if (_myCoil) + coil = ObjectAccessor::GetCreature(*me, _myCoil); + if (!coil) + { + coil = me->FindNearestCreature(NPC_TESLA, 1000.0f, true); + if (coil) + { + _myCoil = coil->GetGUID(); + coil->SetReactState(REACT_PASSIVE); + } + } + return coil; + } - DoMeleeAttackIfReady(); - } + GameObject* myCoilGO() + { + GameObject* coil = nullptr; + if (_myCoilGO) + coil = ObjectAccessor::GetGameObject(*me, _myCoilGO); + if (!coil) + { + coil = me->FindNearestGameObject(GO_CONS_NOX_TESLA_STALAGG, 1000.0f); + if (coil) + _myCoilGO = coil->GetGUID(); + } + return coil; + } + + InstanceScript* instance; + + uint32 powerSurgeTimer; + + ObjectGuid _myCoil; + ObjectGuid _myCoilGO; + bool isOverloading; + bool refreshBeam; + bool isFeignDeath; }; }; @@ -386,142 +718,381 @@ public: struct npc_feugenAI : public ScriptedAI { - npc_feugenAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - staticFieldTimer = 5000; - } + public: + npc_feugenAI(Creature* creature) : ScriptedAI(creature), _myCoil(ObjectGuid::Empty), _myCoilGO(ObjectGuid::Empty), isOverloading(false), refreshBeam(false), isFeignDeath(false) + { + Initialize(); + instance = creature->GetInstanceScript(); + } - InstanceScript* instance; + void Initialize() + { + if (GameObject* coil = myCoilGO()) + coil->SetGoState(GO_STATE_ACTIVE); - uint32 staticFieldTimer; + // if the encounter reset while feigning death + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + isOverloading = false; + isFeignDeath = false; - void Reset() override - { - if (Creature* pThaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) - if (pThaddius->AI()) - pThaddius->AI()->DoAction(ACTION_FEUGEN_RESET); - Initialize(); - } + // force coil state to refresh + refreshBeam = true; - void KilledUnit(Unit* /*victim*/) override - { - if (!(rand32() % 5)) - Talk(SAY_FEUG_SLAY); - } + staticFieldTimer = 6 * IN_MILLISECONDS; + magneticPullTimer = 20 * IN_MILLISECONDS; + } - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_FEUG_AGGRO); - DoCast(SPELL_FEUGEN_TESLA); - } + void Reset() override + { + if (isFeignDeath || !me->IsAlive()) + return; + if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) + thaddius->AI()->DoAction(ACTION_FEUGEN_RESET); + } - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_FEUG_DEATH); - if (Creature* pThaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) - if (pThaddius->AI()) - pThaddius->AI()->DoAction(ACTION_FEUGEN_DIED); - } + void BeginResetEncounter() + { + if (GameObject* coil = myCoilGO()) + coil->SetGoState(GO_STATE_READY); + me->DespawnOrUnsummon(); + me->setActive(false); + } - void UpdateAI(uint32 uiDiff) override - { - if (!UpdateVictim()) - return; + void ResetEncounter() + { + me->Respawn(true); + Initialize(); + } - if (staticFieldTimer <= uiDiff) + void DoAction(int32 action) override { - DoCast(me, SPELL_STATICFIELD); - staticFieldTimer = 5000; - } else staticFieldTimer -= uiDiff; + switch (action) + { + case ACTION_BEGIN_RESET_ENCOUNTER: + BeginResetEncounter(); + break; + case ACTION_RESET_ENCOUNTER: + ResetEncounter(); + break; + case ACTION_FEUGEN_REVIVING_FX: + break; + case ACTION_FEUGEN_REVIVED: + if (!isFeignDeath) + break; + + me->SetFullHealth(); + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Talk(EMOTE_FEIGN_REVIVE); + isFeignDeath = false; + + refreshBeam = true; // force beam refresh + + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + if (stalagg->GetVictim()) + { + me->AddThreat(stalagg->EnsureVictim(), 0.0f); + me->SetInCombatWith(stalagg->EnsureVictim()); + } + staticFieldTimer = 6 * IN_MILLISECONDS; + magneticPullTimer = 30 * IN_MILLISECONDS; + break; + case ACTION_TRANSITION: + me->KillSelf(); // true death this time around + me->DespawnOrUnsummon(); + + if (Creature* coil = myCoil()) + { + coil->CastStop(); + coil->AI()->Talk(EMOTE_TESLA_OVERLOAD); + } + break; + case ACTION_TRANSITION_2: + if (Creature* coil = myCoil()) + if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) + coil->CastSpell(thaddius, SPELL_SHOCK_VISUAL); + break; + case ACTION_TRANSITION_3: + if (GameObject* coil = myCoilGO()) + coil->SetGoState(GO_STATE_READY); + default: + break; + } + } - DoMeleeAttackIfReady(); - } - }; + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_FEUGEN_SLAY); + } -}; + void EnterCombat(Unit* who) override + { + Talk(SAY_FEUGEN_AGGRO); -class spell_thaddius_pos_neg_charge : public SpellScriptLoader -{ - public: - spell_thaddius_pos_neg_charge() : SpellScriptLoader("spell_thaddius_pos_neg_charge") { } + if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) + thaddius->AI()->DoAction(ACTION_STALAGG_AGGRO); - class spell_thaddius_pos_neg_charge_SpellScript : public SpellScript - { - PrepareSpellScript(spell_thaddius_pos_neg_charge_SpellScript); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + if (!stalagg->IsInCombat()) + { + stalagg->AddThreat(who, 0.0f); + stalagg->SetInCombatWith(who); + } + } - bool Validate(SpellInfo const* /*spell*/) override + void DamageTaken(Unit* /*who*/, uint32& damage) override { - if (!sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE)) - return false; - if (!sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_STACK)) - return false; - if (!sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE)) - return false; - if (!sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_STACK)) - return false; - return true; + if (damage < me->GetHealth()) + return; + + if (isFeignDeath) // don't take damage while feigning death + { + damage = 0; + return; + } + + isFeignDeath = true; + isOverloading = false; + + Talk(EMOTE_FEIGN_DEATH); + if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS))) + thaddius->AI()->DoAction(ACTION_FEUGEN_DIED); + + me->RemoveAllAuras(); + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->StopMoving(); + me->SetStandState(UNIT_STAND_STATE_DEAD); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + damage = 0; + + // force beam refresh as we just removed auras + refreshBeam = true; } - bool Load() override + void SpellHit(Unit* caster, SpellInfo const* spell) override { - return GetCaster()->GetTypeId() == TYPEID_UNIT; + if (!caster) + return; + if (spell->Id != SPELL_FEUGEN_TESLA_PERIODIC) + return; + if (!isFeignDeath && me->IsInCombat() && !me->GetHomePosition().IsInDist(me, OVERLOAD_DISTANCE)) + { + if (!isOverloading) + { + isOverloading = true; + caster->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + if (Creature* creatureCaster = caster->ToCreature()) + creatureCaster->AI()->Talk(EMOTE_TESLA_LINK_BREAKS); + me->RemoveAura(SPELL_STALAGG_CHAIN_VISUAL); + } + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + { + caster->CastStop(SPELL_TESLA_SHOCK); + caster->CastSpell(target, SPELL_TESLA_SHOCK,true); + } + } + else if (isOverloading || refreshBeam) + { + isOverloading = false; + refreshBeam = false; + caster->CastStop(); + caster->CastSpell(me, SPELL_FEUGEN_CHAIN_VISUAL, true); + caster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + } } - void HandleTargets(std::list<WorldObject*>& targets) + void UpdateAI(uint32 uiDiff) override { - uint8 count = 0; - for (std::list<WorldObject*>::iterator ihit = targets.begin(); ihit != targets.end(); ++ihit) - if ((*ihit)->GetGUID() != GetCaster()->GetGUID()) - if (Player* target = (*ihit)->ToPlayer()) - if (target->HasAura(GetTriggeringSpell()->Id)) - ++count; + if (isFeignDeath) + return; + if (!UpdateVictim()) + return; - if (count) + if (magneticPullTimer <= uiDiff) + { + DoCast(me, SPELL_MAGNETIC_PULL); + magneticPullTimer = 20 * IN_MILLISECONDS; + } + else magneticPullTimer -= uiDiff; + + if (staticFieldTimer <= uiDiff) { - uint32 spellId = 0; + DoCast(me, SPELL_FEUGEN_STATICFIELD); + staticFieldTimer = 6 * IN_MILLISECONDS; + } + else staticFieldTimer -= uiDiff; - if (GetSpellInfo()->Id == SPELL_POSITIVE_CHARGE) - spellId = SPELL_POSITIVE_CHARGE_STACK; - else // if (GetSpellInfo()->Id == SPELL_NEGATIVE_CHARGE) - spellId = SPELL_NEGATIVE_CHARGE_STACK; + DoMeleeAttackIfReady(); + } - GetCaster()->SetAuraStack(spellId, GetCaster(), count); + private: + Creature* myCoil() + { + Creature* coil = nullptr; + if (_myCoil) + coil = ObjectAccessor::GetCreature(*me, _myCoil); + if (!coil) + { + coil = me->FindNearestCreature(NPC_TESLA, 1000.0f, true); + if (coil) + { + _myCoil = coil->GetGUID(); + coil->SetReactState(REACT_PASSIVE); + } } + return coil; + } + + GameObject* myCoilGO() + { + GameObject* coil = nullptr; + if (_myCoilGO) + coil = ObjectAccessor::GetGameObject(*me, _myCoilGO); + if (!coil) + { + coil = me->FindNearestGameObject(GO_CONS_NOX_TESLA_FEUGEN, 1000.0f); + if (coil) + _myCoilGO = coil->GetGUID(); + } + return coil; + } + InstanceScript* instance; + + uint32 magneticPullTimer; + uint32 staticFieldTimer; + + ObjectGuid _myCoil; + ObjectGuid _myCoilGO; + + bool isOverloading; + bool refreshBeam; + bool isFeignDeath; + }; +}; + +class npc_tesla : public CreatureScript +{ +public: + npc_tesla() : CreatureScript("npc_tesla") { } + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_teslaAI>(creature); + } + + struct npc_teslaAI : public ScriptedAI + { + npc_teslaAI(Creature* creature) : ScriptedAI(creature) { } + + void EnterEvadeMode() override { } // never stop casting due to evade + void UpdateAI(uint32 /*diff*/) override { } // never do anything unless told + void EnterCombat(Unit* /*who*/) override { } + void DamageTaken(Unit* /*who*/, uint32& damage) override { damage = 0; } // no, you can't kill it + }; +}; + +class spell_thaddius_polarity_charge : public SpellScriptLoader +{ + public: + spell_thaddius_polarity_charge() : SpellScriptLoader("spell_thaddius_polarity_charge") { } + + class spell_thaddius_polarity_charge_SpellScript : public SpellScript + { + PrepareSpellScript(spell_thaddius_polarity_charge_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ( + sSpellMgr->GetSpellInfo(SPELL_POLARITY_SHIFT) && + sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_APPLY) && + sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_TICK) && + sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_AMP) && + sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_APPLY) && + sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_TICK) && + sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_AMP) + ); } - void HandleDamage(SpellEffIndex /*effIndex*/) + void HandleTargets(std::list<WorldObject*>& targetList) { if (!GetTriggeringSpell()) return; - Unit* target = GetHitUnit(); - Unit* caster = GetCaster(); + uint32 triggeringId = GetTriggeringSpell()->Id; + uint32 ampId; + switch (triggeringId) + { + case SPELL_POSITIVE_CHARGE_APPLY: + ampId = SPELL_POSITIVE_CHARGE_AMP; + break; + case SPELL_NEGATIVE_CHARGE_APPLY: + ampId = SPELL_NEGATIVE_CHARGE_AMP; + break; + default: + return; + } - if (target->HasAura(GetTriggeringSpell()->Id)) - SetHitDamage(0); - else + uint8 maxStacks = 0; + if (GetCaster()) + switch (GetCaster()->GetMap()->GetDifficulty()) + { + case RAID_DIFFICULTY_10MAN_NORMAL: + maxStacks = MAX_POLARITY_10M; + break; + case RAID_DIFFICULTY_25MAN_NORMAL: + maxStacks = MAX_POLARITY_25M; + break; + default: + break; + } + + uint8 stacksCount = 1; // do we get a stack for our own debuff? + std::list<WorldObject*>::iterator it = targetList.begin(); + while(it != targetList.end()) + { + if ((*it)->GetTypeId() != TYPEID_PLAYER) + { + it = targetList.erase(it); + continue; + } + if ((*it)->ToPlayer()->HasAura(triggeringId)) + { + it = targetList.erase(it); + if (stacksCount < maxStacks) + stacksCount++; + continue; + } + + // this guy will get hit - achievement failure trigger + if (Creature* thaddius = (*it)->FindNearestCreature(NPC_THADDIUS,200.0f)) + thaddius->AI()->DoAction(ACTION_POLARITY_CROSSED); + + ++it; + } + + if (GetCaster() && GetCaster()->ToPlayer()) { - if (target->GetTypeId() == TYPEID_PLAYER && caster->IsAIEnabled) - caster->ToCreature()->AI()->SetData(DATA_POLARITY_SWITCH, 1); + if (!GetCaster()->ToPlayer()->HasAura(ampId)) + GetCaster()->ToPlayer()->AddAura(ampId, GetCaster()); + GetCaster()->ToPlayer()->SetAuraStack(ampId, GetCaster(), stacksCount); } } void Register() override { - OnEffectHitTarget += SpellEffectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thaddius_polarity_charge_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY); } }; SpellScript* GetSpellScript() const override { - return new spell_thaddius_pos_neg_charge_SpellScript(); + return new spell_thaddius_polarity_charge_SpellScript; } }; @@ -536,16 +1107,33 @@ class spell_thaddius_polarity_shift : public SpellScriptLoader bool Validate(SpellInfo const* /*spell*/) override { - if (!sSpellMgr->GetSpellInfo(SPELL_POSITIVE_POLARITY) || !sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_POLARITY)) - return false; - return true; + return ( + sSpellMgr->GetSpellInfo(SPELL_POLARITY_SHIFT) && + sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_APPLY) && + sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_TICK) && + sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_AMP) && + sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_APPLY) && + sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_TICK) && + sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_AMP) + ); } - void HandleDummy(SpellEffIndex /* effIndex */) + void HandleDummy(SpellEffIndex /*effIndex*/) { - Unit* caster = GetCaster(); if (Unit* target = GetHitUnit()) - target->CastSpell(target, roll_chance_i(50) ? SPELL_POSITIVE_POLARITY : SPELL_NEGATIVE_POLARITY, true, NULL, NULL, caster->GetGUID()); + if (target->GetTypeId() == TYPEID_PLAYER) + { + if (roll_chance_i(50)) + { // positive + target->CastSpell(target, SPELL_POSITIVE_CHARGE_APPLY, true); + target->RemoveAura(SPELL_POSITIVE_CHARGE_AMP); + } + else + { // negative + target->CastSpell(target, SPELL_NEGATIVE_CHARGE_APPLY, true); + target->RemoveAura(SPELL_NEGATIVE_CHARGE_AMP); + } + } } void Register() override @@ -560,23 +1148,131 @@ class spell_thaddius_polarity_shift : public SpellScriptLoader } }; -class achievement_polarity_switch : public AchievementCriteriaScript +class spell_thaddius_magnetic_pull : public SpellScriptLoader { public: - achievement_polarity_switch() : AchievementCriteriaScript("achievement_polarity_switch") { } + spell_thaddius_magnetic_pull() : SpellScriptLoader("spell_thaddius_magnetic_pull") { }; + + class spell_thaddius_magnetic_pull_SpellScript : public SpellScript + { + PrepareSpellScript(spell_thaddius_magnetic_pull_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return sSpellMgr->GetSpellInfo(SPELL_MAGNETIC_PULL) ? true : false; + } + + void HandleCast() // only feugen ever casts this according to wowhead data + { + Unit* feugen = GetCaster(); + if (!feugen || feugen->GetEntry() != NPC_FEUGEN) + return; + + Unit* stalagg = ObjectAccessor::GetCreature(*feugen, feugen->GetInstanceScript()->GetGuidData(DATA_STALAGG)); + if (!stalagg) + return; + + Unit* feugenTank = feugen->GetVictim(); + Unit* stalaggTank = stalagg->GetVictim(); + + if (!feugenTank || !stalaggTank) + return; + + ThreatManager& feugenThreat = feugen->getThreatManager(); + ThreatManager& stalaggThreat = stalagg->getThreatManager(); + + if (feugenTank == stalaggTank) // special behavior if the tanks are the same (taken from retail) + { + float feugenTankThreat = feugenThreat.getThreat(feugenTank); + float stalaggTankThreat = stalaggThreat.getThreat(stalaggTank); + + feugenThreat.addThreat(feugenTank, stalaggTankThreat - feugenTankThreat); + stalaggThreat.addThreat(stalaggTank, feugenTankThreat - stalaggTankThreat); + + feugen->CastSpell(stalaggTank, SPELL_MAGNETIC_PULL_EFFECT, true); + } + else // normal case, two tanks + { + float feugenTankThreat = feugenThreat.getThreat(feugenTank); + float feugenOtherThreat = feugenThreat.getThreat(stalaggTank); + float stalaggTankThreat = stalaggThreat.getThreat(stalaggTank); + float stalaggOtherThreat = stalaggThreat.getThreat(feugenTank); + + // set the two entries in feugen's threat table to be equal to the ones in stalagg's + feugenThreat.addThreat(stalaggTank, stalaggTankThreat - feugenOtherThreat); + feugenThreat.addThreat(feugenTank, stalaggOtherThreat - feugenTankThreat); + + // set the two entries in stalagg's threat table to be equal to the ones in feugen's + stalaggThreat.addThreat(feugenTank, feugenTankThreat - stalaggOtherThreat); + stalaggThreat.addThreat(stalaggTank, feugenOtherThreat - stalaggTankThreat); + + // pull the two tanks across + feugenTank->CastSpell(stalaggTank, SPELL_MAGNETIC_PULL_EFFECT, true); + stalaggTank->CastSpell(feugenTank, SPELL_MAGNETIC_PULL_EFFECT, true); + + // and make both attack their respective new tanks + if (feugen->GetAI()) + feugen->GetAI()->AttackStart(stalaggTank); + if (stalagg->GetAI()) + stalagg->GetAI()->AttackStart(feugenTank); + } + } + + void Register() override + { + OnCast += SpellCastFn(spell_thaddius_magnetic_pull_SpellScript::HandleCast); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_thaddius_magnetic_pull_SpellScript(); + } +}; + +class at_thaddius_entrance : public AreaTriggerScript +{ + public: + at_thaddius_entrance() : AreaTriggerScript("at_thaddius_entrance") { } + + bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + { + InstanceScript* instance = player->GetInstanceScript(); + if (!instance || instance->GetData(DATA_HAD_THADDIUS_GREET) || instance->GetBossState(BOSS_THADDIUS) == DONE) + return true; + + if (Creature* thaddius = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_THADDIUS))) + thaddius->AI()->Talk(SAY_GREET); + instance->SetData(DATA_HAD_THADDIUS_GREET, 1u); + + return true; + } +}; + +class achievement_thaddius_shocking : public AchievementCriteriaScript +{ + public: + achievement_thaddius_shocking() : AchievementCriteriaScript("achievement_thaddius_shocking") { } bool OnCheck(Player* /*source*/, Unit* target) override { - return target && target->GetAI()->GetData(DATA_POLARITY_SWITCH); + return target && target->GetAI() && target->GetAI()->GetData(DATA_POLARITY_CROSSED); } }; + void AddSC_boss_thaddius() { new boss_thaddius(); new npc_stalagg(); new npc_feugen(); - new spell_thaddius_pos_neg_charge(); + new npc_tesla(); + + new spell_thaddius_polarity_charge(); new spell_thaddius_polarity_shift(); - new achievement_polarity_switch(); + new spell_thaddius_magnetic_pull(); + + new at_thaddius_entrance(); + + new achievement_thaddius_shocking(); } diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp index 9b10fab2d62..0e572835a51 100644 --- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp +++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp @@ -60,7 +60,6 @@ DoorData const doorData[] = MinionData const minionData[] = { - { NPC_DK_UNDERSTUDY, BOSS_RAZUVIOUS }, { NPC_SIR, BOSS_HORSEMEN }, { NPC_THANE, BOSS_HORSEMEN }, { NPC_LADY, BOSS_HORSEMEN }, @@ -127,6 +126,7 @@ class instance_naxxramas : public InstanceMapScript AbominationCount = 0; hadAnubRekhanGreet = false; hadFaerlinaGreet = false; + hadThaddiusGreet = false; CurrentWingTaunt = SAY_KELTHUZAD_FIRST_WING_TAUNT; playerDied = 0; @@ -142,6 +142,9 @@ class instance_naxxramas : public InstanceMapScript case NPC_FAERLINA: FaerlinaGUID = creature->GetGUID(); break; + case NPC_RAZUVIOUS: + RazuviousGUID = creature->GetGUID(); + break; case NPC_THANE: ThaneGUID = creature->GetGUID(); break; @@ -154,12 +157,12 @@ class instance_naxxramas : public InstanceMapScript case NPC_SIR: SirGUID = creature->GetGUID(); break; - case NPC_THADDIUS: - ThaddiusGUID = creature->GetGUID(); - break; case NPC_HEIGAN: HeiganGUID = creature->GetGUID(); break; + case NPC_THADDIUS: + ThaddiusGUID = creature->GetGUID(); + break; case NPC_FEUGEN: FeugenGUID = creature->GetGUID(); break; @@ -187,6 +190,19 @@ class instance_naxxramas : public InstanceMapScript AddMinion(creature, false); } + void ProcessEvent(WorldObject* /*source*/, uint32 eventId) override + { + switch (eventId) + { + case EVENT_THADDIUS_BEGIN_RESET: + if (GetBossState(BOSS_THADDIUS) == SPECIAL) // this is the initial spawn, we want a shorter spawn time + events.ScheduleEvent(EVENT_THADDIUS_RESET, 5 * IN_MILLISECONDS); + else + events.ScheduleEvent(EVENT_THADDIUS_RESET, 30 * IN_MILLISECONDS); + break; + } + } + void OnGameObjectCreate(GameObject* go) override { if (go->GetGOInfo()->displayId == 6785 || go->GetGOInfo()->displayId == 1287) @@ -329,6 +345,9 @@ class instance_naxxramas : public InstanceMapScript case DATA_HAD_FAERLINA_GREET: hadFaerlinaGreet = (value == 1u); break; + case DATA_HAD_THADDIUS_GREET: + hadThaddiusGreet = (value == 1u); + break; default: break; } @@ -341,9 +360,11 @@ class instance_naxxramas : public InstanceMapScript case DATA_ABOMINATION_KILLED: return AbominationCount; case DATA_HAD_ANUBREKHAN_GREET: - return (uint32)hadAnubRekhanGreet; + return hadAnubRekhanGreet ? 1u : 0u; case DATA_HAD_FAERLINA_GREET: - return (uint32)hadFaerlinaGreet; + return hadFaerlinaGreet ? 1u : 0u; + case DATA_HAD_THADDIUS_GREET: + return hadThaddiusGreet ? 1u : 0u; default: break; } @@ -359,6 +380,8 @@ class instance_naxxramas : public InstanceMapScript return AnubRekhanGUID; case DATA_FAERLINA: return FaerlinaGUID; + case DATA_RAZUVIOUS: + return RazuviousGUID; case DATA_THANE: return ThaneGUID; case DATA_LADY: @@ -367,14 +390,14 @@ class instance_naxxramas : public InstanceMapScript return BaronGUID; case DATA_SIR: return SirGUID; - case DATA_THADDIUS: - return ThaddiusGUID; case DATA_HEIGAN: return HeiganGUID; case DATA_FEUGEN: return FeugenGUID; case DATA_STALAGG: return StalaggGUID; + case DATA_THADDIUS: + return ThaddiusGUID; case DATA_KELTHUZAD: return KelthuzadGUID; case DATA_KELTHUZAD_PORTAL01: @@ -543,6 +566,11 @@ class instance_naxxramas : public InstanceMapScript kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD4); HandleGameObject(KelthuzadDoorGUID, true); break; + case EVENT_THADDIUS_RESET: + if (GetBossState(BOSS_THADDIUS) != DONE) + if (Creature* thaddius = instance->GetCreature(ThaddiusGUID)) + thaddius->AI()->DoAction(-1); + break; default: break; } @@ -628,6 +656,8 @@ class instance_naxxramas : public InstanceMapScript ObjectGuid HeiganGUID; /* The Military Quarter */ + // Instructor Razuvious + ObjectGuid RazuviousGUID; // Gothik the Harvester ObjectGuid GothikGateGUID; // The Four Horsemen @@ -657,6 +687,7 @@ class instance_naxxramas : public InstanceMapScript uint8 AbominationCount; bool hadAnubRekhanGreet; bool hadFaerlinaGreet; + bool hadThaddiusGreet; uint8 CurrentWingTaunt; /* The Immortal / The Undying */ diff --git a/src/server/scripts/Northrend/Naxxramas/naxxramas.h b/src/server/scripts/Northrend/Naxxramas/naxxramas.h index 6289b707411..e4d15cf84ba 100644 --- a/src/server/scripts/Northrend/Naxxramas/naxxramas.h +++ b/src/server/scripts/Northrend/Naxxramas/naxxramas.h @@ -47,8 +47,8 @@ enum Data DATA_GOTHIK_GATE, DATA_SAPPHIRON_BIRTH, DATA_HAD_ANUBREKHAN_GREET, - DATA_HAD_FAERLINA_GREET, + DATA_HAD_THADDIUS_GREET, DATA_HORSEMEN0, DATA_HORSEMEN1, @@ -66,6 +66,7 @@ enum Data64 { DATA_ANUBREKHAN, DATA_FAERLINA, + DATA_RAZUVIOUS, DATA_THANE, DATA_LADY, DATA_BARON, @@ -87,14 +88,16 @@ enum CreaturesIds { NPC_ANUBREKHAN = 15956, NPC_FAERLINA = 15953, + NPC_RAZUVIOUS = 16061, NPC_THANE = 16064, NPC_LADY = 16065, NPC_BARON = 30549, NPC_SIR = 16063, - NPC_THADDIUS = 15928, NPC_HEIGAN = 15936, + NPC_THADDIUS = 15928, NPC_FEUGEN = 15930, NPC_STALAGG = 15929, + NPC_TESLA = 16218, NPC_SAPPHIRON = 15989, NPC_KEL_THUZAD = 15990, NPC_CRYPT_GUARD = 16573, @@ -174,6 +177,10 @@ enum InstanceEvents EVENT_DIALOGUE_GOTHIK_KORTHAZZ2, EVENT_DIALOGUE_GOTHIK_RIVENDARE2, + // Thaddius AI requesting timed encounter (re-)spawn + EVENT_THADDIUS_BEGIN_RESET, + EVENT_THADDIUS_RESET, + // Dialogue that happens after each wing. EVENT_KELTHUZAD_WING_TAUNT, diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index cc9af9413e5..33ffea4993b 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -1244,8 +1244,9 @@ public: ++_wpCount; } else if (Vehicle* hoverDisk = me->GetVehicleKit()) - if (Unit* lordPassenger = hoverDisk->GetPassenger(0)) - lordPassenger->ToCreature()->AI()->DoAction(ACTION_SET_DISK_VICTIM_CHASE); + if (Unit* passenger = hoverDisk->GetPassenger(0)) + if (Creature* lordPassenger = passenger->ToCreature()) + lordPassenger->AI()->DoAction(ACTION_SET_DISK_VICTIM_CHASE); } private: diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index bd4fee60c42..5c852cd7f26 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -701,7 +701,7 @@ class boss_leviathan_mk_ii : public CreatureScript { me->CastStop(); if (Unit* turret = me->GetVehicleKit()->GetPassenger(3)) - turret->Kill(turret); + turret->KillSelf(); me->SetSpeed(MOVE_RUN, 1.5f, true); me->GetMotionMaster()->MovePoint(WP_MKII_P1_IDLE, VehicleRelocation[WP_MKII_P1_IDLE]); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp index 6f07a88536d..8545d5b21eb 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp @@ -2423,7 +2423,7 @@ class spell_yogg_saron_squeeze : public SpellScriptLoader // 64125 { if (Unit* vehicle = GetTarget()->GetVehicleBase()) if (vehicle->IsAlive()) - vehicle->Kill(vehicle); // should tentacle die or just release its target? + vehicle->KillSelf(); // should tentacle die or just release its target? } void Register() override @@ -2905,7 +2905,7 @@ class spell_yogg_saron_insane : public SpellScriptLoader // 63120 void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { if (GetTarget()->IsAlive()) - GetTarget()->Kill(GetTarget()); + GetTarget()->KillSelf(); } void Register() override diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 4cdfa653224..6ef9abe2267 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -1326,7 +1326,7 @@ public: arlos->AI()->Talk(SAY_ARLOS_1); arlos->AI()->Talk(SAY_ARLOS_2); leryssa->AI()->Talk(SAY_LERYSSA_1); - arlos->Kill(arlos, false); + arlos->KillSelf(false); leryssa->RemoveAura(SPELL_STUN); leryssa->ClearUnitState(UNIT_STATE_STUNNED); leryssa->SetWalk(false); diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp index 6e4a8df6f25..5d47e7fa4ec 100644 --- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp +++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp @@ -778,7 +778,7 @@ public: wilhelm->AI()->Talk(SAY_WILHELM_MISS); drostan->AI()->Talk(SAY_DROSTAN_REPLY_MISS); - bird->Kill(bird); + bird->KillSelf(); crunchy->GetMotionMaster()->MovePoint(0, bird->GetPositionX(), bird->GetPositionY(), bird->GetMap()->GetWaterOrGroundLevel(bird->GetPositionX(), bird->GetPositionY(), bird->GetPositionZ())); /// @todo Make crunchy perform emote eat when he reaches the bird diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_grandmaster_vorpil.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_grandmaster_vorpil.cpp index b16a9630335..04d3a20d945 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_grandmaster_vorpil.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_grandmaster_vorpil.cpp @@ -257,7 +257,7 @@ class npc_voidtraveler : public CreatureScript { DoCastAOE(SPELL_EMPOWERING_SHADOWS, true); DoCast(me, SPELL_SHADOW_NOVA, true); - me->Kill(me); + me->KillSelf(); return; } me->GetMotionMaster()->MoveFollow(Vorpil, 0, 0); diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp index 787b191e23d..0292ed65697 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp @@ -633,7 +633,7 @@ public: } if (Creature* vashj = ObjectAccessor::GetCreature(*me, VashjGUID)) if (!vashj->IsInCombat() || ENSURE_AI(boss_lady_vashj::boss_lady_vashjAI, vashj->AI())->Phase != 2 || vashj->isDead()) - me->Kill(me); + me->KillSelf(); Move = 1000; } else Move -= diff; } diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index 872269fd19f..2d99fd9bacf 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -1286,7 +1286,7 @@ class npc_kael_flamestrike : public CreatureScript DoCast(me, SPELL_FLAME_STRIKE_DMG); } else - me->Kill(me); + me->KillSelf(); KillSelf = true; Timer = 1000; diff --git a/src/server/scripts/Pet/pet_mage.cpp b/src/server/scripts/Pet/pet_mage.cpp index 0d6353cfb86..14d2687c298 100644 --- a/src/server/scripts/Pet/pet_mage.cpp +++ b/src/server/scripts/Pet/pet_mage.cpp @@ -24,11 +24,25 @@ #include "ScriptedCreature.h" #include "CombatAI.h" #include "Pet.h" +#include "PetAI.h" +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" enum MageSpells { SPELL_MAGE_CLONE_ME = 45204, - SPELL_MAGE_MASTERS_THREAT_LIST = 58838 + SPELL_MAGE_MASTERS_THREAT_LIST = 58838, + SPELL_MAGE_FROST_BOLT = 59638, + SPELL_MAGE_FIRE_BLAST = 59637 +}; + +enum MirrorImageTimers +{ + TIMER_MIRROR_IMAGE_INIT = 0, + TIMER_MIRROR_IMAGE_FROST_BOLT = 4000, + TIMER_MIRROR_IMAGE_FIRE_BLAST = 6000 }; class npc_pet_mage_mirror_image : public CreatureScript @@ -40,20 +54,184 @@ class npc_pet_mage_mirror_image : public CreatureScript { npc_pet_mage_mirror_imageAI(Creature* creature) : CasterAI(creature) { } + void Init() + { + Unit* owner = me->GetCharmerOrOwner(); + + std::list<Unit*> targets; + Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, 30.0f); + Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(me, targets, u_check); + me->VisitNearbyObject(40.0f, searcher); + + Unit* highestThreatUnit = nullptr; + float highestThreat = 0.0f; + Unit* nearestPlayer = nullptr; + for (std::list<Unit*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) + { + // Consider only units without CC + if (!(*iter)->HasBreakableByDamageCrowdControlAura((*iter))) + { + // Take first found unit + if (!highestThreatUnit && (*iter)->GetTypeId() != TYPEID_PLAYER) + { + highestThreatUnit = (*iter); + continue; + } + if (!nearestPlayer && ((*iter)->GetTypeId() == TYPEID_PLAYER)) + { + nearestPlayer = (*iter); + continue; + } + // else compare best fit unit with current unit + ThreatContainer::StorageType triggers = (*iter)->getThreatManager().getThreatList(); + for (ThreatContainer::StorageType::const_iterator trig_citr = triggers.begin(); trig_citr != triggers.end(); ++trig_citr) + { + // Try to find threat referenced to owner + if ((*trig_citr)->getTarget() == owner) + { + // Check if best fit hostile unit hs lower threat than this current unit + if (highestThreat < (*trig_citr)->getThreat()) + { + // If so, update best fit unit + highestThreat = (*trig_citr)->getThreat(); + highestThreatUnit = (*iter); + break; + } + } + } + // In case no unit with threat was found so far, always check for nearest unit (only for players) + if ((*iter)->GetTypeId() == TYPEID_PLAYER) + { + // If this player is closer than the previous one, update it + if (me->GetDistance((*iter)->GetPosition()) < me->GetDistance(nearestPlayer->GetPosition())) + nearestPlayer = (*iter); + } + } + } + // Prioritize units with threat referenced to owner + if (highestThreat > 0.0f && highestThreatUnit) + me->Attack(highestThreatUnit, false); + // If there is no such target, try to attack nearest hostile unit if such exists + else if (nearestPlayer) + me->Attack(nearestPlayer, false); + } + + bool IsInThreatList(Unit* target) + { + Unit* owner = me->GetCharmerOrOwner(); + + std::list<Unit*> targets; + Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, 30.0f); + Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(me, targets, u_check); + me->VisitNearbyObject(40.0f, searcher); + + for (std::list<Unit*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) + { + if ((*iter) == target) + { + // Consider only units without CC + if (!(*iter)->HasBreakableByDamageCrowdControlAura((*iter))) + { + ThreatContainer::StorageType triggers = (*iter)->getThreatManager().getThreatList(); + for (ThreatContainer::StorageType::const_iterator trig_citr = triggers.begin(); trig_citr != triggers.end(); ++trig_citr) + { + // Try to find threat referenced to owner + if ((*trig_citr)->getTarget() == owner) + return true; + } + } + } + } + return false; + } + void InitializeAI() override { CasterAI::InitializeAI(); Unit* owner = me->GetOwner(); if (!owner) return; - // Inherit Master's Threat List (not yet implemented) - owner->CastSpell((Unit*)NULL, SPELL_MAGE_MASTERS_THREAT_LIST, true); + // here mirror image casts on summoner spell (not present in client dbc) 49866 // here should be auras (not present in client dbc): 35657, 35658, 35659, 35660 selfcast by mirror images (stats related?) // Clone Me! owner->CastSpell(me, SPELL_MAGE_CLONE_ME, false); } + void EnterCombat(Unit* who) override + { + if (me->GetVictim() && !me->GetVictim()->HasBreakableByDamageCrowdControlAura(me)) + { + me->CastSpell(who, SPELL_MAGE_FIRE_BLAST, false); + events.ScheduleEvent(SPELL_MAGE_FROST_BOLT, TIMER_MIRROR_IMAGE_INIT); + events.ScheduleEvent(SPELL_MAGE_FIRE_BLAST, TIMER_MIRROR_IMAGE_FIRE_BLAST); + } + else + EnterEvadeMode(); + } + + void Reset() override + { + events.Reset(); + } + + void UpdateAI(uint32 diff) override + { + Unit* owner = me->GetCharmerOrOwner(); + Unit* target = owner->getAttackerForHelper(); + + events.Update(diff); + + // prevent CC interrupts by images + if (me->GetVictim() && me->EnsureVictim()->HasBreakableByDamageCrowdControlAura(me)) + { + me->InterruptNonMeleeSpells(false); + return; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (!owner) + return; + + // assign target if image doesnt have any or the target is not actual + if (!target || me->GetVictim() != target) + { + Unit* ownerTarget = nullptr; + if (Player* owner = me->GetCharmerOrOwner()->ToPlayer()) + ownerTarget = owner->GetSelectedUnit(); + + // recognize which victim will be choosen + if (ownerTarget && ownerTarget->GetTypeId() == TYPEID_PLAYER) + { + if (!ownerTarget->HasBreakableByDamageCrowdControlAura(ownerTarget)) + me->Attack(ownerTarget, false); + } + else if (ownerTarget && (ownerTarget->GetTypeId() != TYPEID_PLAYER) && IsInThreatList(ownerTarget)) + { + if (!ownerTarget->HasBreakableByDamageCrowdControlAura(ownerTarget)) + me->Attack(ownerTarget, false); + } + else + Init(); + } + + if (uint32 spellId = events.ExecuteEvent()) + { + if (spellId == SPELL_MAGE_FROST_BOLT) + { + events.ScheduleEvent(SPELL_MAGE_FROST_BOLT, TIMER_MIRROR_IMAGE_FROST_BOLT); + DoCastVictim(spellId); + } + else if (spellId == SPELL_MAGE_FIRE_BLAST) + { + DoCastVictim(spellId); + events.ScheduleEvent(SPELL_MAGE_FIRE_BLAST, TIMER_MIRROR_IMAGE_FIRE_BLAST); + } + } + } + // Do not reload Creature templates on evade mode enter - prevent visual lost void EnterEvadeMode() override { @@ -68,6 +246,7 @@ class npc_pet_mage_mirror_image : public CreatureScript me->GetMotionMaster()->Clear(false); me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE); } + Init(); } }; diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index cd0052c24bc..88271dc7139 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -1343,6 +1343,15 @@ class spell_dk_raise_dead : public SpellScriptLoader GetCaster()->CastSpell(targets, spellInfo, NULL, TRIGGERED_FULL_MASK); } + void OverrideCooldown() + { + // Because the ghoul is summoned by one of triggered spells SendCooldownEvent is not sent for this spell + // but the client has locked it by itself so we need some link between this spell and the real spell summoning. + // Luckily such link already exists - spell category + // This starts infinite category cooldown which can later be used by SendCooldownEvent to send packet for this spell + GetCaster()->GetSpellHistory()->StartCooldown(GetSpellInfo(), 0, nullptr, true); + } + void Register() override { OnCheckCast += SpellCheckCastFn(spell_dk_raise_dead_SpellScript::CheckCast); @@ -1351,6 +1360,7 @@ class spell_dk_raise_dead : public SpellScriptLoader OnCast += SpellCastFn(spell_dk_raise_dead_SpellScript::ConsumeReagents); OnEffectHitTarget += SpellEffectFn(spell_dk_raise_dead_SpellScript::HandleRaiseDead, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); OnEffectHitTarget += SpellEffectFn(spell_dk_raise_dead_SpellScript::HandleRaiseDead, EFFECT_2, SPELL_EFFECT_DUMMY); + AfterCast += SpellCastFn(spell_dk_raise_dead_SpellScript::OverrideCooldown); } private: diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp index a1b218010aa..e29f87a5ff9 100644 --- a/src/server/scripts/Spells/spell_holiday.cpp +++ b/src/server/scripts/Spells/spell_holiday.cpp @@ -277,6 +277,86 @@ class spell_hallow_end_tricky_treat : public SpellScriptLoader } }; +// Hallowed wands +enum HallowendData +{ + //wand spells + SPELL_HALLOWED_WAND_PIRATE = 24717, + SPELL_HALLOWED_WAND_NINJA = 24718, + SPELL_HALLOWED_WAND_LEPER_GNOME = 24719, + SPELL_HALLOWED_WAND_RANDOM = 24720, + SPELL_HALLOWED_WAND_SKELETON = 24724, + SPELL_HALLOWED_WAND_WISP = 24733, + SPELL_HALLOWED_WAND_GHOST = 24737, + SPELL_HALLOWED_WAND_BAT = 24741 +}; + +class spell_hallow_end_wand : public SpellScriptLoader +{ +public: + spell_hallow_end_wand() : SpellScriptLoader("spell_hallow_end_wand") {} + + class spell_hallow_end_wand_SpellScript : public SpellScript + { + PrepareSpellScript(spell_hallow_end_wand_SpellScript); + + bool Validate(SpellInfo const* /*spellEntry*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PIRATE_COSTUME_MALE) || + !sSpellMgr->GetSpellInfo(SPELL_PIRATE_COSTUME_FEMALE) || + !sSpellMgr->GetSpellInfo(SPELL_NINJA_COSTUME_MALE) || + !sSpellMgr->GetSpellInfo(SPELL_NINJA_COSTUME_FEMALE) || + !sSpellMgr->GetSpellInfo(SPELL_LEPER_GNOME_COSTUME_MALE) || + !sSpellMgr->GetSpellInfo(SPELL_LEPER_GNOME_COSTUME_FEMALE) || + !sSpellMgr->GetSpellInfo(SPELL_GHOST_COSTUME_MALE) || + !sSpellMgr->GetSpellInfo(SPELL_GHOST_COSTUME_FEMALE)) + return false; + return true; + } + + void HandleScriptEffect() + { + Unit* caster = GetCaster(); + Unit* target = GetHitUnit(); + + uint32 spellId = 0; + uint8 gender = target->getGender(); + + switch (GetSpellInfo()->Id) + { + case SPELL_HALLOWED_WAND_LEPER_GNOME: + spellId = gender ? SPELL_LEPER_GNOME_COSTUME_FEMALE : SPELL_LEPER_GNOME_COSTUME_MALE; + break; + case SPELL_HALLOWED_WAND_PIRATE: + spellId = gender ? SPELL_PIRATE_COSTUME_FEMALE : SPELL_PIRATE_COSTUME_MALE; + break; + case SPELL_HALLOWED_WAND_GHOST: + spellId = gender ? SPELL_GHOST_COSTUME_FEMALE : SPELL_GHOST_COSTUME_MALE; + break; + case SPELL_HALLOWED_WAND_NINJA: + spellId = gender ? SPELL_NINJA_COSTUME_FEMALE : SPELL_NINJA_COSTUME_MALE; + break; + case SPELL_HALLOWED_WAND_RANDOM: + spellId = RAND(SPELL_HALLOWED_WAND_PIRATE, SPELL_HALLOWED_WAND_NINJA, SPELL_HALLOWED_WAND_LEPER_GNOME, SPELL_HALLOWED_WAND_SKELETON, SPELL_HALLOWED_WAND_WISP, SPELL_HALLOWED_WAND_GHOST, SPELL_HALLOWED_WAND_BAT); + break; + default: + return; + } + caster->CastSpell(target, spellId, true); + } + + void Register() override + { + AfterHit += SpellHitFn(spell_hallow_end_wand_SpellScript::HandleScriptEffect); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_hallow_end_wand_SpellScript(); + } +}; + enum PilgrimsBountyBuffFood { // Pilgrims Bounty Buff Food @@ -886,6 +966,7 @@ void AddSC_holiday_spell_scripts() new spell_hallow_end_trick(); new spell_hallow_end_trick_or_treat(); new spell_hallow_end_tricky_treat(); + new spell_hallow_end_wand(); // Pilgrims Bounty new spell_pilgrims_bounty_buff_food("spell_gen_slow_roasted_turkey", SPELL_WELL_FED_AP_TRIGGER); new spell_pilgrims_bounty_buff_food("spell_gen_cranberry_chutney", SPELL_WELL_FED_ZM_TRIGGER); diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index f2871871c30..0ca4112b288 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -480,7 +480,7 @@ class spell_hun_misdirection : public SpellScriptLoader void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT) + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT || !GetTarget()->HasAura(SPELL_HUNTER_MISDIRECTION_PROC)) GetTarget()->ResetRedirectThreat(); } diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 717382a0e36..f0b6c0945c2 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -2509,7 +2509,7 @@ class spell_item_chicken_cover : public SpellScriptLoader if (!target->HasAura(SPELL_CHICKEN_NET) && (caster->GetQuestStatus(QUEST_CHICKEN_PARTY) == QUEST_STATUS_INCOMPLETE || caster->GetQuestStatus(QUEST_FLOWN_THE_COOP) == QUEST_STATUS_INCOMPLETE)) { caster->CastSpell(caster, SPELL_CAPTURE_CHICKEN_ESCAPE, true); - target->Kill(target); + target->KillSelf(); } } } diff --git a/src/server/scripts/World/duel_reset.cpp b/src/server/scripts/World/duel_reset.cpp index f08469d5bd5..9e720455692 100644 --- a/src/server/scripts/World/duel_reset.cpp +++ b/src/server/scripts/World/duel_reset.cpp @@ -17,6 +17,8 @@ #include "ScriptMgr.h" #include "Player.h" +#include "Pet.h" +#include "SpellInfo.h" class DuelResetScript : public PlayerScript { @@ -26,28 +28,89 @@ class DuelResetScript : public PlayerScript // Called when a duel starts (after 3s countdown) void OnDuelStart(Player* player1, Player* player2) override { + // Cooldowns reset if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS)) { player1->GetSpellHistory()->SaveCooldownStateBeforeDuel(); player2->GetSpellHistory()->SaveCooldownStateBeforeDuel(); - player1->RemoveArenaSpellCooldowns(true); - player2->RemoveArenaSpellCooldowns(true); + + ResetSpellCooldowns(player1); + ResetSpellCooldowns(player2); + } + + // Health and mana reset + if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_HEALTH_MANA)) + { + player1->SaveHealthBeforeDuel(); + player1->SetHealth(player1->GetMaxHealth()); + + player2->SaveHealthBeforeDuel(); + player2->SetHealth(player2->GetMaxHealth()); + + // check if player1 class uses mana + if (player1->getPowerType() == POWER_MANA || player1->getClass() == CLASS_DRUID) + { + player1->SaveManaBeforeDuel(); + player1->SetPower(POWER_MANA, player1->GetMaxPower(POWER_MANA)); + } + + // check if player2 class uses mana + if (player2->getPowerType() == POWER_MANA || player2->getClass() == CLASS_DRUID) + { + player2->SaveManaBeforeDuel(); + player2->SetPower(POWER_MANA, player2->GetMaxPower(POWER_MANA)); + } } } // Called when a duel ends - void OnDuelEnd(Player* winner, Player* loser, DuelCompleteType /*type*/) override + void OnDuelEnd(Player* winner, Player* loser, DuelCompleteType type) override { - if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS)) + // do not reset anything if DUEL_INTERRUPTED or DUEL_FLED + if (type == DUEL_WON) { - winner->RemoveArenaSpellCooldowns(true); - loser->RemoveArenaSpellCooldowns(true); + // Cooldown restore + if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS)) + { + + ResetSpellCooldowns(winner); + ResetSpellCooldowns(loser); - winner->GetSpellHistory()->RestoreCooldownStateAfterDuel(); - loser->GetSpellHistory()->RestoreCooldownStateAfterDuel(); + winner->GetSpellHistory()->RestoreCooldownStateAfterDuel(); + loser->GetSpellHistory()->RestoreCooldownStateAfterDuel(); + } + + // Health and mana restore + if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_HEALTH_MANA)) + { + winner->RestoreHealthAfterDuel(); + loser->RestoreHealthAfterDuel(); + + // check if player1 class uses mana + if (winner->getPowerType() == POWER_MANA || winner->getClass() == CLASS_DRUID) + winner->RestoreManaAfterDuel(); + + // check if player2 class uses mana + if (loser->getPowerType() == POWER_MANA || loser->getClass() == CLASS_DRUID) + loser->RestoreManaAfterDuel(); + } } } + + static void ResetSpellCooldowns(Player* player) + { + // remove cooldowns on spells that have < 10 min CD and has no onHold + player->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool + { + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); + return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS && !itr->second.OnHold; + }, true); + + // pet cooldowns + if (Pet* pet = player->GetPet()) + pet->GetSpellHistory()->ResetAllCooldowns(); + } }; void AddSC_duel_reset() |
