diff options
Diffstat (limited to 'src')
111 files changed, 4732 insertions, 4448 deletions
diff --git a/src/server/database/Database/DatabaseLoader.cpp b/src/server/database/Database/DatabaseLoader.cpp index 1d704100d93..d3e9e7a7132 100644 --- a/src/server/database/Database/DatabaseLoader.cpp +++ b/src/server/database/Database/DatabaseLoader.cpp @@ -66,7 +66,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st if (error) { TC_LOG_ERROR("sql.driver", "\nDatabasePool %s NOT opened. There were errors opening the MySQL connections. Check your SQLDriverLogFile " - "for specific errors. Read wiki at http://collab.kpsn.org/display/tc/TrinityCore+Home", name.c_str()); + "for specific errors. Read wiki at http://www.trinitycore.info/display/tc/TrinityCore+Home", name.c_str()); return false; } diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 3399e9a8136..3ecc3975611 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -662,7 +662,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() // PvPstats PrepareStatement(CHAR_SEL_PVPSTATS_MAXID, "SELECT MAX(id) FROM pvpstats_battlegrounds", CONNECTION_SYNCH); PrepareStatement(CHAR_INS_PVPSTATS_BATTLEGROUND, "INSERT INTO pvpstats_battlegrounds (id, winner_faction, bracket_id, type, date) VALUES (?, ?, ?, ?, NOW())", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_PVPSTATS_PLAYER, "INSERT INTO pvpstats_players (battleground_id, character_guid, score_killing_blows, score_deaths, score_honorable_kills, score_bonus_honor, score_damage_done, score_healing_done, attr_1, attr_2, attr_3, attr_4, attr_5) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_PVPSTATS_PLAYER, "INSERT INTO pvpstats_players (battleground_id, character_guid, winner, score_killing_blows, score_deaths, score_honorable_kills, score_bonus_honor, score_damage_done, score_healing_done, attr_1, attr_2, attr_3, attr_4, attr_5) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_PVPSTATS_FACTIONS_OVERALL, "SELECT winner_faction, COUNT(*) AS count FROM pvpstats_battlegrounds WHERE DATEDIFF(NOW(), date) < 7 GROUP BY winner_faction ORDER BY winner_faction ASC", CONNECTION_SYNCH); // QuestTracker diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 7e199659177..9b2837bfffe 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -39,10 +39,10 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, " + PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, " "bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id " "FROM account a LEFT JOIN account r ON a.id = r.recruiter LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id " - "LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id " + "LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 " "WHERE a.username = ? ORDER BY aa.RealmID DESC LIMIT 1", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH); @@ -51,7 +51,7 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_INS_IP_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_IP_NOT_BANNED, "DELETE FROM ip_banned WHERE ip = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_ACCOUNT_BANNED, "INSERT INTO account_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?, 1)", CONNECTION_ASYNC); - PrepareStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED, "UPDATE account_banned SET unbandate = UNIX_TIMESTAMP(), active = 0 WHERE id = ? AND active != 0", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED, "UPDATE account_banned SET active = 0 WHERE id = ? AND active != 0", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM, "DELETE FROM realmcharacters WHERE acctid = ? AND realmid = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_REALM_CHARACTERS, "DELETE FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_REALM_CHARACTERS, "INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (?, ?, ?)", CONNECTION_ASYNC); @@ -118,12 +118,12 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_INFO, "SELECT " BnetAccountInfo ", ba.sha_pass_hash, ba.v, ba.s, " BnetGameAccountInfo " FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account" - " LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ?", CONNECTION_ASYNC); + " LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_VS_FIELDS, "UPDATE battlenet_accounts SET v = ?, s = ? WHERE email = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_SESSION_KEY, "UPDATE battlenet_accounts SET sessionKey = ?, online = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_BNET_RECONNECT_INFO, "SELECT " BnetAccountInfo ", ba.sessionKey, " BnetGameAccountInfo " FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account" - " LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? AND a.username = ?", CONNECTION_ASYNC); + " LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? AND a.username = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_FAILED_LOGINS, "UPDATE battlenet_accounts SET failed_logins = failed_logins + 1 WHERE email = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO, "UPDATE battlenet_accounts SET last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS, "SELECT rc.numchars, r.id, r.Region, r.Battlegroup, r.gamebuild FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?", CONNECTION_ASYNC); diff --git a/src/server/game/AI/CreatureAIFactory.h b/src/server/game/AI/CreatureAIFactory.h index 4473d3e9cd5..4e11630259b 100644 --- a/src/server/game/AI/CreatureAIFactory.h +++ b/src/server/game/AI/CreatureAIFactory.h @@ -50,6 +50,8 @@ CreatureAIFactory<REAL_AI>::Create(void* data) const typedef FactoryHolder<CreatureAI> CreatureAICreator; typedef FactoryHolder<CreatureAI>::FactoryHolderRegistry CreatureAIRegistry; +#define sCreatureAIRegistry CreatureAIRegistry::instance() + //GO struct SelectableGameObjectAI : public FactoryHolder<GameObjectAI>, public Permissible<GameObject> { @@ -76,4 +78,7 @@ GameObjectAIFactory<REAL_GO_AI>::Create(void* data) const typedef FactoryHolder<GameObjectAI> GameObjectAICreator; typedef FactoryHolder<GameObjectAI>::FactoryHolderRegistry GameObjectAIRegistry; + +#define sGameObjectAIRegistry GameObjectAIRegistry::instance() + #endif diff --git a/src/server/game/AI/CreatureAIImpl.h b/src/server/game/AI/CreatureAIImpl.h index a2c5c5db057..529f7420021 100644 --- a/src/server/game/AI/CreatureAIImpl.h +++ b/src/server/game/AI/CreatureAIImpl.h @@ -23,292 +23,14 @@ #include "CreatureAI.h" #include "SpellMgr.h" -template<class T> -inline -const T& RAND(const T& v1, const T& v2) -{ - return (urand(0, 1)) ? v1 : v2; -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3) -{ - switch (urand(0, 2)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4) -{ - switch (urand(0, 3)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5) -{ - switch (urand(0, 4)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6) -{ - switch (urand(0, 5)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7) -{ - switch (urand(0, 6)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8) -{ - switch (urand(0, 7)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9) -{ - switch (urand(0, 8)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10) -{ - switch (urand(0, 9)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11) -{ - switch (urand(0, 10)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11, const T& v12) -{ - switch (urand(0, 11)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - case 11: return v12; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11, const T& v12, const T& v13) -{ - switch (urand(0, 12)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - case 11: return v12; - case 12: return v13; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14) -{ - switch (urand(0, 13)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - case 11: return v12; - case 12: return v13; - case 13: return v14; - } -} - -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14, const T& v15) -{ - switch (urand(0, 14)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - case 11: return v12; - case 12: return v13; - case 13: return v14; - case 14: return v15; - } -} +#include <functional> +#include <type_traits> -template<class T> -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14, const T& v15, const T& v16) +template<typename First, typename Second, typename... Rest> +static inline First const& RAND(First const& first, Second const& second, Rest const&... rest) { - switch (urand(0, 15)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - case 11: return v12; - case 12: return v13; - case 13: return v14; - case 14: return v15; - case 15: return v16; - } + std::reference_wrapper<typename std::add_const<First>::type> const pack[] = { first, second, rest... }; + return pack[urand(0, sizeof...(rest) + 1)].get(); } enum AITarget diff --git a/src/server/game/AI/CreatureAISelector.cpp b/src/server/game/AI/CreatureAISelector.cpp index ddb5ba3508b..495e6dcee31 100644 --- a/src/server/game/AI/CreatureAISelector.cpp +++ b/src/server/game/AI/CreatureAISelector.cpp @@ -30,10 +30,9 @@ namespace FactorySelector CreatureAI* selectAI(Creature* creature) { const CreatureAICreator* ai_factory = NULL; - CreatureAIRegistry& ai_registry(*CreatureAIRegistry::instance()); if (creature->IsPet()) - ai_factory = ai_registry.GetRegistryItem("PetAI"); + ai_factory = sCreatureAIRegistry->GetRegistryItem("PetAI"); //scriptname in db if (!ai_factory) @@ -43,32 +42,32 @@ namespace FactorySelector // AIname in db std::string ainame=creature->GetAIName(); if (!ai_factory && !ainame.empty()) - ai_factory = ai_registry.GetRegistryItem(ainame); + ai_factory = sCreatureAIRegistry->GetRegistryItem(ainame); // select by NPC flags if (!ai_factory) { if (creature->IsVehicle()) - ai_factory = ai_registry.GetRegistryItem("VehicleAI"); + ai_factory = sCreatureAIRegistry->GetRegistryItem("VehicleAI"); else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER) - ai_factory = ai_registry.GetRegistryItem("PetAI"); + ai_factory = sCreatureAIRegistry->GetRegistryItem("PetAI"); else if (creature->HasFlag64(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK)) - ai_factory = ai_registry.GetRegistryItem("NullCreatureAI"); + ai_factory = sCreatureAIRegistry->GetRegistryItem("NullCreatureAI"); else if (creature->IsGuard()) - ai_factory = ai_registry.GetRegistryItem("GuardAI"); + ai_factory = sCreatureAIRegistry->GetRegistryItem("GuardAI"); else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) - ai_factory = ai_registry.GetRegistryItem("PetAI"); + ai_factory = sCreatureAIRegistry->GetRegistryItem("PetAI"); else if (creature->IsTotem()) - ai_factory = ai_registry.GetRegistryItem("TotemAI"); + ai_factory = sCreatureAIRegistry->GetRegistryItem("TotemAI"); else if (creature->IsTrigger()) { if (creature->m_spells[0]) - ai_factory = ai_registry.GetRegistryItem("TriggerAI"); + ai_factory = sCreatureAIRegistry->GetRegistryItem("TriggerAI"); else - ai_factory = ai_registry.GetRegistryItem("NullCreatureAI"); + ai_factory = sCreatureAIRegistry->GetRegistryItem("NullCreatureAI"); } else if (creature->IsCritter() && !creature->HasUnitTypeMask(UNIT_MASK_GUARDIAN)) - ai_factory = ai_registry.GetRegistryItem("CritterAI"); + ai_factory = sCreatureAIRegistry->GetRegistryItem("CritterAI"); } // select by permit check @@ -76,7 +75,7 @@ namespace FactorySelector { int best_val = -1; typedef CreatureAIRegistry::RegistryMapType RMT; - RMT const& l = ai_registry.GetRegisteredItems(); + RMT const& l = sCreatureAIRegistry->GetRegisteredItems(); for (RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter) { const CreatureAICreator* factory = iter->second; @@ -128,14 +127,13 @@ namespace FactorySelector GameObjectAI* SelectGameObjectAI(GameObject* go) { - const GameObjectAICreator* ai_factory = NULL; - GameObjectAIRegistry& ai_registry(*GameObjectAIRegistry::instance()); + GameObjectAICreator const* ai_factory = NULL; // scriptname in db if (GameObjectAI* scriptedAI = sScriptMgr->GetGameObjectAI(go)) return scriptedAI; - ai_factory = ai_registry.GetRegistryItem(go->GetAIName()); + ai_factory = sGameObjectAIRegistry->GetRegistryItem(go->GetAIName()); //future goAI types go here diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 512385a67b6..28f9ad591df 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -446,7 +446,13 @@ BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature), instance(creature->GetInstanceScript()), summons(creature), _boundary(instance ? instance->GetBossBoundary(bossId) : NULL), - _bossId(bossId) { } + _bossId(bossId) +{ + scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); +} void BossAI::_Reset() { @@ -457,6 +463,7 @@ void BossAI::_Reset() me->ResetLootMode(); events.Reset(); summons.DespawnAll(); + scheduler.CancelAll(); if (instance) instance->SetBossState(_bossId, NOT_STARTED); } @@ -465,15 +472,13 @@ void BossAI::_JustDied() { events.Reset(); summons.DespawnAll(); + scheduler.CancelAll(); if (instance) instance->SetBossState(_bossId, DONE); } void BossAI::_EnterCombat() { - me->SetCombatPulseDelay(5); - me->setActive(true); - DoZoneInCombat(); if (instance) { // bosses do not respawn, check only on enter combat @@ -484,6 +489,11 @@ void BossAI::_EnterCombat() } instance->SetBossState(_bossId, IN_PROGRESS); } + + me->SetCombatPulseDelay(5); + me->setActive(true); + DoZoneInCombat(); + ScheduleTasks(); } void BossAI::TeleportCheaters() diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 74d0654dff2..49e2e0e8518 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -23,6 +23,7 @@ #include "CreatureAI.h" #include "CreatureAIImpl.h" #include "InstanceScript.h" +#include "TaskScheduler.h" #define CAST_AI(a, b) (dynamic_cast<a*>(b)) #define ENSURE_AI(a,b) (EnsureAI<a>(b)) @@ -356,6 +357,8 @@ class BossAI : public ScriptedAI // is supposed to run more than once virtual void ExecuteEvent(uint32 /*eventId*/) { } + virtual void ScheduleTasks() { } + void Reset() override { _Reset(); } void EnterCombat(Unit* /*who*/) override { _EnterCombat(); } void JustDied(Unit* /*killer*/) override { _JustDied(); } @@ -382,6 +385,7 @@ class BossAI : public ScriptedAI EventMap events; SummonList summons; + TaskScheduler scheduler; private: BossBoundaryMap const* const _boundary; diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h index 1d71652c948..deabd1dc484 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h @@ -68,8 +68,8 @@ struct npc_escortAI : public ScriptedAI void EnterEvadeMode() override; - void UpdateAI(uint32 diff) override; //the "internal" update, calls UpdateEscortAI() - virtual void UpdateEscortAI(uint32 const diff); //used when it's needed to add code in update (abilities, scripted events, etc) + void UpdateAI(uint32 diff) override; // the "internal" update, calls UpdateEscortAI() + virtual void UpdateEscortAI(uint32 diff); // used when it's needed to add code in update (abilities, scripted events, etc) void MovementInform(uint32, uint32) override; diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 9baa4caeea7..4c630d44c48 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -726,6 +726,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_TICKET_RESET_COMPLAINT = 832, RBAC_PERM_COMMAND_TICKET_RESET_SUGGESTION = 833, RBAC_PERM_COMMAND_GO_QUEST = 834, + RBAC_PERM_COMMAND_DEBUG_LOADCELLS = 835, // custom permissions 1000+ RBAC_PERM_MAX diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index 04fbc45e709..4f1bae9eccd 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -3132,7 +3132,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT) TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` has ScriptName set for non-scripted data type (Entry: %u, type %u), useless data.", criteria_id, dataType); else - scriptId = sObjectMgr->GetScriptId(scriptName.c_str()); + scriptId = sObjectMgr->GetScriptId(scriptName); } AchievementCriteriaData data(dataType, fields[2].GetUInt32(), fields[3].GetUInt32(), scriptId); diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 07c88486c50..82e3aafc098 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -835,19 +835,20 @@ void Battleground::EndBattleground(uint32 winner) stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PVPSTATS_PLAYER); BattlegroundScoreMap::const_iterator score = PlayerScores.find(player->GetGUID()); - stmt->setUInt32(0, battlegroundId); - stmt->setUInt64(1, player->GetGUID().GetCounter()); - stmt->setUInt32(2, score->second->GetKillingBlows()); - stmt->setUInt32(3, score->second->GetDeaths()); - stmt->setUInt32(4, score->second->GetHonorableKills()); - stmt->setUInt32(5, score->second->GetBonusHonor()); - stmt->setUInt32(6, score->second->GetDamageDone()); - stmt->setUInt32(7, score->second->GetHealingDone()); - stmt->setUInt32(8, score->second->GetAttr1()); - stmt->setUInt32(9, score->second->GetAttr2()); - stmt->setUInt32(10, score->second->GetAttr3()); - stmt->setUInt32(11, score->second->GetAttr4()); - stmt->setUInt32(12, score->second->GetAttr5()); + stmt->setUInt32(0, battlegroundId); + stmt->setUInt64(1, player->GetGUID().GetCounter()); + stmt->setBool (2, team == winner); + stmt->setUInt32(3, score->second->GetKillingBlows()); + stmt->setUInt32(4, score->second->GetDeaths()); + stmt->setUInt32(5, score->second->GetHonorableKills()); + stmt->setUInt32(6, score->second->GetBonusHonor()); + stmt->setUInt32(7, score->second->GetDamageDone()); + stmt->setUInt32(8, score->second->GetHealingDone()); + stmt->setUInt32(9, score->second->GetAttr1()); + stmt->setUInt32(10, score->second->GetAttr2()); + stmt->setUInt32(11, score->second->GetAttr3()); + stmt->setUInt32(12, score->second->GetAttr4()); + stmt->setUInt32(13, score->second->GetAttr5()); CharacterDatabase.Execute(stmt); } @@ -1250,48 +1251,58 @@ void Battleground::RemoveFromBGFreeSlotQueue() // returns the number how many players can join battleground to MaxPlayersPerTeam uint32 Battleground::GetFreeSlotsForTeam(uint32 Team) const { - // if BG is starting ... invite anyone - if (GetStatus() == STATUS_WAIT_JOIN) + // if BG is starting and CONFIG_BATTLEGROUND_INVITATION_TYPE == BG_QUEUE_INVITATION_TYPE_NO_BALANCE, invite anyone + if (GetStatus() == STATUS_WAIT_JOIN && sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == BG_QUEUE_INVITATION_TYPE_NO_BALANCE) return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; - // if BG is already started .. do not allow to join too much players of one faction - uint32 otherTeam; - uint32 otherIn; + + // if BG is already started or CONFIG_BATTLEGROUND_INVITATION_TYPE != BG_QUEUE_INVITATION_TYPE_NO_BALANCE, do not allow to join too much players of one faction + uint32 otherTeamInvitedCount; + uint32 thisTeamInvitedCount; + uint32 otherTeamPlayersCount; + uint32 thisTeamPlayersCount; + if (Team == ALLIANCE) { - otherTeam = GetInvitedCount(HORDE); - otherIn = GetPlayersCountByTeam(HORDE); + thisTeamInvitedCount = GetInvitedCount(ALLIANCE); + otherTeamInvitedCount = GetInvitedCount(HORDE); + thisTeamPlayersCount = GetPlayersCountByTeam(ALLIANCE); + otherTeamPlayersCount = GetPlayersCountByTeam(HORDE); } else { - otherTeam = GetInvitedCount(ALLIANCE); - otherIn = GetPlayersCountByTeam(ALLIANCE); + thisTeamInvitedCount = GetInvitedCount(HORDE); + otherTeamInvitedCount = GetInvitedCount(ALLIANCE); + thisTeamPlayersCount = GetPlayersCountByTeam(HORDE); + otherTeamPlayersCount = GetPlayersCountByTeam(ALLIANCE); } - if (GetStatus() == STATUS_IN_PROGRESS) + if (GetStatus() == STATUS_IN_PROGRESS || GetStatus() == STATUS_WAIT_JOIN) { // difference based on ppl invited (not necessarily entered battle) // default: allow 0 uint32 diff = 0; - // allow join one person if the sides are equal (to fill up bg to minplayersperteam) - if (otherTeam == GetInvitedCount(Team)) + + // allow join one person if the sides are equal (to fill up bg to minPlayerPerTeam) + if (otherTeamInvitedCount == thisTeamInvitedCount) diff = 1; // allow join more ppl if the other side has more players - else if (otherTeam > GetInvitedCount(Team)) - diff = otherTeam - GetInvitedCount(Team); + else if (otherTeamInvitedCount > thisTeamInvitedCount) + diff = otherTeamInvitedCount - thisTeamInvitedCount; // difference based on max players per team (don't allow inviting more) - uint32 diff2 = (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; + uint32 diff2 = (thisTeamInvitedCount < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - thisTeamInvitedCount : 0; + // difference based on players who already entered // default: allow 0 uint32 diff3 = 0; - // allow join one person if the sides are equal (to fill up bg minplayersperteam) - if (otherIn == GetPlayersCountByTeam(Team)) + // allow join one person if the sides are equal (to fill up bg minPlayerPerTeam) + if (otherTeamPlayersCount == thisTeamPlayersCount) diff3 = 1; // allow join more ppl if the other side has more players - else if (otherIn > GetPlayersCountByTeam(Team)) - diff3 = otherIn - GetPlayersCountByTeam(Team); + else if (otherTeamPlayersCount > thisTeamPlayersCount) + diff3 = otherTeamPlayersCount - thisTeamPlayersCount; // or other side has less than minPlayersPerTeam - else if (GetInvitedCount(Team) <= GetMinPlayersPerTeam()) - diff3 = GetMinPlayersPerTeam() - GetInvitedCount(Team) + 1; + else if (thisTeamInvitedCount <= GetMinPlayersPerTeam()) + diff3 = GetMinPlayersPerTeam() - thisTeamInvitedCount + 1; // return the minimum of the 3 differences diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index 74a9ac32b1b..afa6403cc22 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -502,7 +502,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates() float dist = fields[7].GetFloat(); bgTemplate.MaxStartDistSq = dist * dist; bgTemplate.Weight = fields[8].GetUInt8(); - bgTemplate.ScriptId = sObjectMgr->GetScriptId(fields[9].GetCString()); + bgTemplate.ScriptId = sObjectMgr->GetScriptId(fields[9].GetString()); bgTemplate.BattlemasterEntry = bl; if (bgTemplate.MaxPlayersPerTeam == 0 || bgTemplate.MinPlayersPerTeam > bgTemplate.MaxPlayersPerTeam) diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp index 8f459432d01..ab38b89baac 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp +++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp @@ -497,24 +497,51 @@ void BattlegroundQueue::FillPlayersToBG(Battleground* bg, BattlegroundBracketId { int32 hordeFree = bg->GetFreeSlotsForTeam(HORDE); int32 aliFree = bg->GetFreeSlotsForTeam(ALLIANCE); + uint32 aliCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].size(); + uint32 hordeCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].size(); + + // try to get even teams + if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == BG_QUEUE_INVITATION_TYPE_EVEN) + { + // check if the teams are even + if (hordeFree == 1 && aliFree == 1) + { + // if we are here, the teams have the same amount of players + // then we have to allow to join the same amount of players + int32 hordeExtra = hordeCount - aliCount; + int32 aliExtra = aliCount - hordeCount; + + hordeExtra = std::max(hordeExtra, 0); + aliExtra = std::max(aliExtra, 0); + + if (aliCount != hordeCount) + { + aliFree -= aliExtra; + hordeFree -= hordeExtra; + + aliFree = std::max(aliFree, 0); + hordeFree = std::max(hordeFree, 0); + } + } + } //iterator for iterating through bg queue GroupsQueueType::const_iterator Ali_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].begin(); //count of groups in queue - used to stop cycles - uint32 aliCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].size(); + //index to queue which group is current uint32 aliIndex = 0; for (; aliIndex < aliCount && m_SelectionPools[TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); aliIndex++) ++Ali_itr; //the same thing for horde GroupsQueueType::const_iterator Horde_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].begin(); - uint32 hordeCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].size(); + uint32 hordeIndex = 0; for (; hordeIndex < hordeCount && m_SelectionPools[TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); hordeIndex++) ++Horde_itr; //if ofc like BG queue invitation is set in config, then we are happy - if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == 0) + if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == BG_QUEUE_INVITATION_TYPE_NO_BALANCE) return; /* @@ -649,7 +676,7 @@ bool BattlegroundQueue::CheckNormalMatch(Battleground* /*bg_template*/, Battlegr uint32 j = TEAM_ALLIANCE; if (m_SelectionPools[TEAM_HORDE].GetPlayerCount() < m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount()) j = TEAM_HORDE; - if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != 0 + if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != BG_QUEUE_INVITATION_TYPE_NO_BALANCE && m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= minPlayers) { //we will try to invite more groups to team with less players indexed by j diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.h b/src/server/game/Battlegrounds/BattlegroundQueue.h index eb225250a56..2a64e1cb324 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.h +++ b/src/server/game/Battlegrounds/BattlegroundQueue.h @@ -64,6 +64,13 @@ enum BattlegroundQueueGroupTypes }; #define BG_QUEUE_GROUP_TYPES_COUNT 4 +enum BattlegroundQueueInvitationType +{ + BG_QUEUE_INVITATION_TYPE_NO_BALANCE = 0, // no balance: N+M vs N players + BG_QUEUE_INVITATION_TYPE_BALANCED = 1, // teams balanced: N+1 vs N players + BG_QUEUE_INVITATION_TYPE_EVEN = 2 // teams even: N vs N players +}; + class Battleground; class BattlegroundQueue { diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h index cc208277ba3..9dc232c4010 100644 --- a/src/server/game/Chat/Chat.h +++ b/src/server/game/Chat/Chat.h @@ -43,7 +43,7 @@ class ChatCommand public: ChatCommand(char const* name, uint32 permission, bool allowConsole, pHandler handler, std::string help, std::vector<ChatCommand> childCommands = std::vector<ChatCommand>()) - : Name(name), Permission(permission), AllowConsole(allowConsole), Handler(handler), Help(std::move(help)), ChildCommands(std::move(childCommands)) { } + : Name(ASSERT_NOTNULL(name)), Permission(permission), AllowConsole(allowConsole), Handler(handler), Help(std::move(help)), ChildCommands(std::move(childCommands)) { } char const* Name; uint32 Permission; // function pointer required correct align (use uint32) diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index ede1dfca9e1..84830e07eed 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -996,7 +996,7 @@ void ConditionMgr::LoadConditions(bool isReload) cond->NegativeCondition = fields[10].GetBool(); cond->ErrorType = fields[11].GetUInt32(); cond->ErrorTextId = fields[12].GetUInt32(); - cond->ScriptId = sObjectMgr->GetScriptId(fields[13].GetCString()); + cond->ScriptId = sObjectMgr->GetScriptId(fields[13].GetString()); if (iConditionTypeOrReference >= 0) cond->ConditionType = ConditionTypes(iConditionTypeOrReference); diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 68798d43e38..0e835e02dc2 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -1273,14 +1273,6 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false* if (player->GetMapId() == uint32(dungeon->map)) player->TeleportToBGEntryPoint(); - // in the case were we are the last in lfggroup then we must disband when porting out of the instance - if (group && group->GetMembersCount() == 1) - { - group->Disband(); - TC_LOG_DEBUG("lfg.teleport", "Player %s is last in lfggroup so we disband the group.", - player->GetName().c_str()); - } - return; } diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp index 42e1f4a545f..224cec71d86 100644 --- a/src/server/game/DungeonFinding/LFGScripts.cpp +++ b/src/server/game/DungeonFinding/LFGScripts.cpp @@ -101,7 +101,17 @@ void LFGPlayerScript::OnMapChanged(Player* player) player->CastSpell(player, LFG_SPELL_LUCK_OF_THE_DRAW, true); } else + { + Group* group = player->GetGroup(); + if (group && group->GetMembersCount() == 1) + { + sLFGMgr->LeaveLfg(group->GetGUID()); + group->Disband(); + TC_LOG_DEBUG("lfg", "LFGPlayerScript::OnMapChanged, Player %s(%s) is last in the lfggroup so we disband the group.", + player->GetName().c_str(), player->GetGUID().ToString().c_str()); + } player->RemoveAurasDueToSpell(LFG_SPELL_LUCK_OF_THE_DRAW); + } } LFGGroupScript::LFGGroupScript() : GroupScript("LFGGroupScript") { } diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index ffa924cc0ea..955f03b6c8e 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -790,6 +790,22 @@ bool Item::CanBeTraded(bool mail, bool trade) const return true; } +void Item::SetCount(uint32 value) +{ + SetUInt32Value(ITEM_FIELD_STACK_COUNT, value); + + if (Player* player = GetOwner()) + { + if (TradeData* tradeData = player->GetTradeData()) + { + TradeSlots slot = tradeData->GetTradeSlotForItem(GetGUID()); + + if (slot != TRADE_SLOT_INVALID) + tradeData->SetItem(slot, this, true); + } + } +} + bool Item::HasEnchantRequiredSkill(const Player* player) const { // Check all enchants for required skill diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 37d7e3613f2..3452951d554 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -318,7 +318,7 @@ class Item : public Object bool GemsFitSockets() const; uint32 GetCount() const { return GetUInt32Value(ITEM_FIELD_STACK_COUNT); } - void SetCount(uint32 value) { SetUInt32Value(ITEM_FIELD_STACK_COUNT, value); } + void SetCount(uint32 value); uint32 GetMaxStackCount() const { return GetTemplate()->GetMaxStackSize(); } uint8 GetGemCountWithID(uint32 GemID) const; uint8 GetGemCountWithLimitCategory(uint32 limitCategory) const; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index ab2d4fbdbd0..576c86fbcc5 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -165,13 +165,13 @@ Item* TradeData::GetSpellCastItem() const return !m_spellCastItem.IsEmpty() ? m_player->GetItemByGuid(m_spellCastItem) : NULL; } -void TradeData::SetItem(TradeSlots slot, Item* item) +void TradeData::SetItem(TradeSlots slot, Item* item, bool update /*= false*/) { ObjectGuid itemGuid; if (item) itemGuid = item->GetGUID(); - if (m_items[slot] == itemGuid) + if (m_items[slot] == itemGuid && !update) return; m_items[slot] = itemGuid; @@ -9267,6 +9267,17 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) packet.Worldstates.emplace_back(4131, 0); // 10 WORLDSTATE_ALGALON_DESPAWN_TIMER } break; + // Violet Hold + case 4415: + if (instance && mapid == 608) + instance->FillInitialWorldStates(packet); + else + { + packet.Worldstates.emplace_back(3816, 0); // 9 WORLD_STATE_VH_SHOW + packet.Worldstates.emplace_back(3815, 100); // 10 WORLD_STATE_VH_PRISON_STATE + packet.Worldstates.emplace_back(3810, 0); // 11 WORLD_STATE_VH_WAVE_COUNT + } + break; // Halls of Refection case 4820: if (instance && mapid == 668) @@ -15299,6 +15310,16 @@ bool Player::GetQuestRewardStatus(uint32 quest_id) const Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id); if (qInfo) { + if (qInfo->IsSeasonal() && !qInfo->IsRepeatable()) + { + uint16 eventId = sGameEventMgr->GetEventIdForQuest(qInfo); + if (m_seasonalquests.find(eventId) != m_seasonalquests.end()) + return m_seasonalquests.find(eventId)->second.find(quest_id) != m_seasonalquests.find(eventId)->second.end(); + + return false; + } + + // for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once if (!qInfo->IsRepeatable()) return m_RewardedQuests.find(quest_id) != m_RewardedQuests.end(); @@ -15317,8 +15338,17 @@ QuestStatus Player::GetQuestStatus(uint32 quest_id) const return itr->second.Status; if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id)) + { + if (qInfo->IsSeasonal() && !qInfo->IsRepeatable()) + { + uint16 eventId = sGameEventMgr->GetEventIdForQuest(qInfo); + if (m_seasonalquests.find(eventId) == m_seasonalquests.end() || m_seasonalquests.find(eventId)->second.find(quest_id) == m_seasonalquests.find(eventId)->second.end()) + return QUEST_STATUS_NONE; + } + if (!qInfo->IsRepeatable() && m_RewardedQuests.find(quest_id) != m_RewardedQuests.end()) return QUEST_STATUS_REWARDED; + } } return QUEST_STATUS_NONE; } @@ -18722,7 +18752,7 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setString(index++, GetName()); stmt->setUInt8(index++, getRace()); stmt->setUInt8(index++, getClass()); - stmt->setUInt8(index++, getGender()); + stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect stmt->setUInt8(index++, getLevel()); stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP)); stmt->setUInt64(index++, GetMoney()); @@ -18844,7 +18874,7 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setString(index++, GetName()); stmt->setUInt8(index++, getRace()); stmt->setUInt8(index++, getClass()); - stmt->setUInt8(index++, getGender()); + stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect stmt->setUInt8(index++, getLevel()); stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP)); stmt->setUInt64(index++, GetMoney()); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 6b981528d63..e78c12f9cf6 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -28,6 +28,7 @@ #include "PetDefines.h" #include "QuestDef.h" #include "SpellMgr.h" +#include "SpellHistory.h" #include "Unit.h" #include "Opcodes.h" #include "WorldSession.h" @@ -1145,7 +1146,7 @@ class TradeData Item* GetItem(TradeSlots slot) const; bool HasItem(ObjectGuid itemGuid) const; TradeSlots GetTradeSlotForItem(ObjectGuid itemGuid) const; - void SetItem(TradeSlots slot, Item* item); + void SetItem(TradeSlots slot, Item* item, bool update = false); uint32 GetSpell() const { return m_spell; } void SetSpell(uint32 spell_id, Item* castItem = NULL); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index a1a1afe6f74..c15fe26f60e 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -11901,7 +11901,7 @@ void CharmInfo::InitPossessCreateSpells() { uint32 spellId = _unit->ToCreature()->m_spells[i]; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (spellInfo && !spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD)) + if (spellInfo) { if (spellInfo->IsPassive()) _unit->CastSpell(_unit, spellInfo, true); @@ -11929,7 +11929,7 @@ void CharmInfo::InitCharmCreateSpells() uint32 spellId = _unit->ToCreature()->m_spells[x]; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (!spellInfo || spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD)) + if (!spellInfo) { _charmspells[x].SetActionAndType(spellId, ACT_DISABLED); continue; @@ -12778,6 +12778,9 @@ bool Unit::IsPolymorphed() const void Unit::SetDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_DISPLAYID, modelId); + // Set Gender by modelId + if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId)) + SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender); } void Unit::RestoreDisplayId() diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 3e418f08880..b650cef7a7f 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -524,7 +524,7 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) creatureTemplate.RegenHealth = fields[74].GetBool(); creatureTemplate.MechanicImmuneMask = fields[75].GetUInt32(); creatureTemplate.flags_extra = fields[76].GetUInt32(); - creatureTemplate.ScriptID = GetScriptId(fields[77].GetCString()); + creatureTemplate.ScriptID = GetScriptId(fields[77].GetString()); } void ObjectMgr::LoadCreatureTemplateAddons() @@ -2733,7 +2733,7 @@ void ObjectMgr::LoadItemScriptNames() continue; } - _itemTemplateStore[itemId].ScriptId = GetScriptId(fields[1].GetCString()); + _itemTemplateStore[itemId].ScriptId = GetScriptId(fields[1].GetString()); ++count; } while (result->NextRow()); } @@ -4935,8 +4935,8 @@ void ObjectMgr::LoadSpellScriptNames() Field* fields = result->Fetch(); - int32 spellId = fields[0].GetInt32(); - char const* scriptName = fields[1].GetCString(); + int32 spellId = fields[0].GetInt32(); + std::string const scriptName = fields[1].GetString(); bool allRanks = false; if (spellId < 0) @@ -4948,18 +4948,18 @@ void ObjectMgr::LoadSpellScriptNames() SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) does not exist.", scriptName, fields[0].GetInt32()); + TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) does not exist.", scriptName.c_str(), fields[0].GetInt32()); continue; } if (allRanks) { if (!spellInfo->IsRanked()) - TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) has no ranks of spell.", scriptName, fields[0].GetInt32()); + TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) has no ranks of spell.", scriptName.c_str(), fields[0].GetInt32()); if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) { - TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is not first rank of spell.", scriptName, fields[0].GetInt32()); + TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is not first rank of spell.", scriptName.c_str(), fields[0].GetInt32()); continue; } while (spellInfo) @@ -4971,7 +4971,7 @@ void ObjectMgr::LoadSpellScriptNames() else { if (spellInfo->IsRanked()) - TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName, spellId); + TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName.c_str(), spellId); _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName))); } @@ -5009,7 +5009,7 @@ void ObjectMgr::ValidateSpellScripts() bool valid = true; if (!spellScript && !auraScript) { - TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(sitr->second->second)); + TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(sitr->second->second).c_str()); valid = false; } if (spellScript) @@ -5149,7 +5149,7 @@ void ObjectMgr::LoadInstanceTemplate() instanceTemplate.AllowMount = fields[3].GetBool(); instanceTemplate.Parent = uint32(fields[1].GetUInt16()); - instanceTemplate.ScriptId = sObjectMgr->GetScriptId(fields[2].GetCString()); + instanceTemplate.ScriptId = sObjectMgr->GetScriptId(fields[2].GetString()); _instanceTemplateStore[mapID] = instanceTemplate; @@ -5641,8 +5641,8 @@ void ObjectMgr::LoadAreaTriggerScripts() { Field* fields = result->Fetch(); - uint32 triggerId = fields[0].GetUInt32(); - char const* scriptName = fields[1].GetCString(); + uint32 triggerId = fields[0].GetUInt32(); + std::string const scriptName = fields[1].GetString(); AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(triggerId); if (!atEntry) @@ -6533,7 +6533,7 @@ void ObjectMgr::LoadGameObjectTemplate() got.unkInt32 = fields[43].GetInt32(); got.AIName = fields[44].GetString(); - got.ScriptId = GetScriptId(fields[45].GetCString()); + got.ScriptId = GetScriptId(fields[45].GetString()); // Checks @@ -8527,11 +8527,17 @@ void ObjectMgr::LoadScriptNames() TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " ScriptNames in %u ms", _scriptNamesStore.size(), GetMSTimeDiffToNow(oldMSTime)); } -uint32 ObjectMgr::GetScriptId(char const* name) +std::string const& ObjectMgr::GetScriptName(uint32 id) const +{ + static std::string const empty = ""; + return id < _scriptNamesStore.size() ? _scriptNamesStore[id] : empty; +} + +uint32 ObjectMgr::GetScriptId(std::string const& name) { // use binary search to find the script name in the sorted vector // assume "" is the first element - if (!name) + if (name.empty()) return 0; ScriptNameContainer::const_iterator itr = std::lower_bound(_scriptNamesStore.begin(), _scriptNamesStore.end(), name); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index c6f155dbff5..6b3712e0b92 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1273,8 +1273,8 @@ class ObjectMgr bool IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount, uint32 ptime, uint32 ExtendedCost, uint8 type, Player* player = NULL, std::set<uint32>* skip_vendors = NULL, uint32 ORnpcflag = 0) const; void LoadScriptNames(); - char const* GetScriptName(uint32 id) const { return id < _scriptNamesStore.size() ? _scriptNamesStore[id].c_str() : ""; } - uint32 GetScriptId(char const* name); + std::string const& GetScriptName(uint32 id) const; + uint32 GetScriptId(std::string const& name); SpellClickInfoMapBounds GetSpellClickInfoMapBounds(uint32 creature_id) const { diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 76303c063e5..33c15e271c7 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -115,8 +115,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPackets::Quest::QuestG if (Player* playerQuestObject = object->ToPlayer()) { - if ((!_player->GetDivider().IsEmpty() && _player->GetDivider() != packet.QuestGiverGUID) || - ((object != _player && !playerQuestObject->CanShareQuest(packet.QuestID)))) + if ((_player->GetDivider().IsEmpty() && _player->GetDivider() != packet.QuestGiverGUID) || !playerQuestObject->CanShareQuest(packet.QuestID)) { CLOSE_GOSSIP_CLEAR_DIVIDER(); return; diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp index 80024879a43..122c08acc1d 100644 --- a/src/server/game/Instances/InstanceScript.cpp +++ b/src/server/game/Instances/InstanceScript.cpp @@ -319,6 +319,11 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state) return false; } +bool InstanceScript::_SkipCheckRequiredBosses(Player const* player /*= nullptr*/) const +{ + return player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES); +} + void InstanceScript::Load(char const* data) { if (!data) @@ -654,3 +659,25 @@ void InstanceScript::UpdatePhasing() if (Player* player = itr->GetSource()) player->SendUpdatePhasing(); } + +std::string InstanceScript::GetBossStateName(uint8 state) +{ + // See enum EncounterState in InstanceScript.h + switch (state) + { + case NOT_STARTED: + return "NOT_STARTED"; + case IN_PROGRESS: + return "IN_PROGRESS"; + case FAIL: + return "FAIL"; + case DONE: + return "DONE"; + case SPECIAL: + return "SPECIAL"; + case TO_BE_DECIDED: + return "TO_BE_DECIDED"; + default: + return "INVALID"; + } +} diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index d932c24abf3..d5fa76a1b06 100644 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -224,6 +224,7 @@ class InstanceScript : public ZoneScript virtual bool SetBossState(uint32 id, EncounterState state); EncounterState GetBossState(uint32 id) const { return id < bosses.size() ? bosses[id].state : TO_BE_DECIDED; } + static std::string GetBossStateName(uint8 state); BossBoundaryMap const* GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : NULL; } // Achievement criteria additional requirements check @@ -280,6 +281,8 @@ class InstanceScript : public ZoneScript void WriteSaveDataBossStates(std::ostringstream& data); virtual void WriteSaveDataMore(std::ostringstream& /*data*/) { } + bool _SkipCheckRequiredBosses(Player const* player = nullptr) const; + private: static void LoadObjectData(ObjectData const* creatureData, ObjectInfoMap& objectInfo); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 12ce3e1d13a..e625cc9c70b 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -302,19 +302,19 @@ template<> void Map::AddToGrid(Corpse* obj, Cell const& cell) { NGridType* grid = getNGrid(cell.GridX(), cell.GridY()); - if (obj->IsWorldObject()) - { - // Corpses are a special object type - they can be added to grid via a call to AddToMap - // or loaded through ObjectGridLoader. - // Both corpses loaded from database and these freshly generated by Player::CreateCoprse are added to _corpsesByCell - // ObjectGridLoader loads all corpses from _corpsesByCell even if they were already added to grid before it was loaded - // so we need to explicitly check it here (Map::AddToGrid is only called from Player::BuildPlayerRepop, not from ObjectGridLoader) - // to avoid failing an assertion in GridObject::AddToGrid - if (grid->isGridObjectDataLoaded()) + // Corpses are a special object type - they can be added to grid via a call to AddToMap + // or loaded through ObjectGridLoader. + // Both corpses loaded from database and these freshly generated by Player::CreateCoprse are added to _corpsesByCell + // ObjectGridLoader loads all corpses from _corpsesByCell even if they were already added to grid before it was loaded + // so we need to explicitly check it here (Map::AddToGrid is only called from Player::BuildPlayerRepop, not from ObjectGridLoader) + // to avoid failing an assertion in GridObject::AddToGrid + if (grid->isGridObjectDataLoaded()) + { + if (obj->IsWorldObject()) grid->GetGridType(cell.CellX(), cell.CellY()).AddWorldObject(obj); + else + grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj); } - else - grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj); // bones. nothing special here } template<class T> @@ -3213,7 +3213,7 @@ void InstanceMap::CreateInstanceData(bool load) i_data->SetCompletedEncountersMask(fields[1].GetUInt32()); if (!data.empty()) { - TC_LOG_DEBUG("maps", "Loading instance data for `%s` with id %u", sObjectMgr->GetScriptName(i_script_id), i_InstanceId); + TC_LOG_DEBUG("maps", "Loading instance data for `%s` with id %u", sObjectMgr->GetScriptName(i_script_id).c_str(), i_InstanceId); i_data->Load(data.c_str()); } } diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 625ad8cda49..7455ad91913 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -388,7 +388,8 @@ enum TrinityStrings LANG_COMMAND_CHEAT_POWER = 361, LANG_COMMAND_CHEAT_WW = 362, LANG_COMMAND_WHISPEROFFPLAYER = 363, - // Room for more level 2 364-399 not used + LANG_COMMAND_CHEAT_TAXINODES = 364, + // Room for more level 2 365-399 not used // level 3 chat LANG_SCRIPTS_RELOADED = 400, @@ -1210,5 +1211,6 @@ enum TrinityStrings LANG_NPCINFO_INHABIT_TYPE = 11008, LANG_NPCINFO_FLAGS_EXTRA = 11009 + }; #endif diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 117f4b675bf..ad6f1b457aa 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -417,6 +417,24 @@ void MotionMaster::MoveCirclePath(float x, float y, float z, float radius, bool init.Launch(); } +void MotionMaster::MoveSmoothPath(uint32 pointId, G3D::Vector3 const* pathPoints, size_t pathSize, bool walk) +{ + Movement::PointsArray path(pathPoints, pathPoints + pathSize); + + Movement::MoveSplineInit init(_owner); + init.MovebyPath(path); + init.SetSmooth(); + init.SetWalk(walk); + init.Launch(); + + // This code is not correct + // EffectMovementGenerator does not affect UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE + // need to call PointMovementGenerator with various pointIds + Mutate(new EffectMovementGenerator(pointId), MOTION_SLOT_ACTIVE); + //Position pos(pathPoints[pathSize - 1].x, pathPoints[pathSize - 1].y, pathPoints[pathSize - 1].z); + //MovePoint(EVENT_CHARGE_PREPATH, pos, false); +} + void MotionMaster::MoveFall(uint32 id /*=0*/) { // use larger distance for vmap height search than in most other cases diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index c8da50364ba..9a8950de9f7 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -185,6 +185,7 @@ class MotionMaster //: private std::stack<MovementGenerator *> { MoveJump(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speedXY, speedZ, id); } void MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id = EVENT_JUMP); void MoveCirclePath(float x, float y, float z, float radius, bool clockwise, uint8 stepCount); + void MoveSmoothPath(uint32 pointId, G3D::Vector3 const* pathPoints, size_t pathSize, bool walk); void MoveFall(uint32 id = 0); void MoveSeekAssistance(float x, float y, float z); diff --git a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp index b07d21436b2..65b9f67342a 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp +++ b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp @@ -71,7 +71,7 @@ void OutdoorPvPMgr::InitOutdoorPvP() OutdoorPvPData* data = new OutdoorPvPData(); OutdoorPvPTypes realTypeId = OutdoorPvPTypes(typeId); data->TypeId = realTypeId; - data->ScriptId = sObjectMgr->GetScriptId(fields[1].GetCString()); + data->ScriptId = sObjectMgr->GetScriptId(fields[1].GetString()); m_OutdoorPvPDatas[realTypeId] = data; ++count; diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index aa8be4946e4..40e522a185e 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -93,6 +93,7 @@ void AddSC_npcs_special(); void AddSC_npc_taxi(); void AddSC_achievement_scripts(); void AddSC_action_ip_logger(); +void AddSC_duel_reset(); //eastern kingdoms void AddSC_alterac_valley(); //Alterac Valley @@ -113,11 +114,8 @@ void AddSC_blackrock_caverns(); void AddSC_instance_blackrock_caverns(); void AddSC_blackrock_depths(); //Blackrock Depths void AddSC_boss_ambassador_flamelash(); -void AddSC_boss_anubshiah(); void AddSC_boss_draganthaurissan(); void AddSC_boss_general_angerforge(); -void AddSC_boss_gorosh_the_dervish(); -void AddSC_boss_grizzle(); void AddSC_boss_high_interrogator_gerstahn(); void AddSC_boss_magmus(); void AddSC_boss_moira_bronzebeard(); @@ -812,6 +810,7 @@ void AddWorldScripts() // To avoid duplicate code, we check once /*ONLY*/ if logging is permitted or not. if (sWorld->getBoolConfig(CONFIG_IP_BASED_ACTION_LOGGING)) AddSC_action_ip_logger(); // location: scripts\World\action_ip_logger.cpp + AddSC_duel_reset(); #endif } @@ -836,11 +835,8 @@ void AddEasternKingdomsScripts() AddSC_instance_blackrock_caverns(); AddSC_blackrock_depths(); //Blackrock Depths AddSC_boss_ambassador_flamelash(); - AddSC_boss_anubshiah(); AddSC_boss_draganthaurissan(); AddSC_boss_general_angerforge(); - AddSC_boss_gorosh_the_dervish(); - AddSC_boss_grizzle(); AddSC_boss_high_interrogator_gerstahn(); AddSC_boss_magmus(); AddSC_boss_moira_bronzebeard(); diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 0267061bc69..4ad2e8af23d 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -41,6 +41,60 @@ UnusedScriptNamesContainer UnusedScriptNames; // } +// Trait which indicates whether this script type +// must be assigned in the database. +template<typename> +struct is_script_database_bound + : std::false_type { }; + +template<> +struct is_script_database_bound<SpellScriptLoader> + : std::true_type { }; + +template<> +struct is_script_database_bound<InstanceMapScript> + : std::true_type { }; + +template<> +struct is_script_database_bound<ItemScript> + : std::true_type { }; + +template<> +struct is_script_database_bound<CreatureScript> + : std::true_type { }; + +template<> +struct is_script_database_bound<GameObjectScript> + : std::true_type { }; + +template<> +struct is_script_database_bound<AreaTriggerScript> + : std::true_type { }; + +template<> +struct is_script_database_bound<BattlegroundScript> + : std::true_type { }; + +template<> +struct is_script_database_bound<OutdoorPvPScript> + : std::true_type { }; + +template<> +struct is_script_database_bound<WeatherScript> + : std::true_type { }; + +template<> +struct is_script_database_bound<ConditionScript> + : std::true_type { }; + +template<> +struct is_script_database_bound<TransportScript> + : std::true_type { }; + +template<> +struct is_script_database_bound<AchievementCriteriaScript> + : std::true_type { }; + // This is the global static registry of scripts. template<class TScript> class ScriptRegistry @@ -71,78 +125,83 @@ class ScriptRegistry } } - if (script->IsDatabaseBound()) + AddScript(is_script_database_bound<TScript>{}, script); + } + + // Gets a script by its ID (assigned by ObjectMgr). + static TScript* GetScriptById(uint32 id) + { + ScriptMapIterator it = ScriptPointerList.find(id); + if (it != ScriptPointerList.end()) + return it->second; + + return NULL; + } + + private: + + // Adds a database bound script + static void AddScript(std::true_type, TScript* const script) + { + // Get an ID for the script. An ID only exists if it's a script that is assigned in the database + // through a script name (or similar). + uint32 id = sObjectMgr->GetScriptId(script->GetName()); + if (id) { - // Get an ID for the script. An ID only exists if it's a script that is assigned in the database - // through a script name (or similar). - uint32 id = sObjectMgr->GetScriptId(script->GetName().c_str()); - if (id) + // Try to find an existing script. + bool existing = false; + for (ScriptMapIterator it = ScriptPointerList.begin(); it != ScriptPointerList.end(); ++it) { - // Try to find an existing script. - bool existing = false; - for (ScriptMapIterator it = ScriptPointerList.begin(); it != ScriptPointerList.end(); ++it) - { - // If the script names match... - if (it->second->GetName() == script->GetName()) - { - // ... It exists. - existing = true; - break; - } - } - - // If the script isn't assigned -> assign it! - if (!existing) + // If the script names match... + if (it->second->GetName() == script->GetName()) { - ScriptPointerList[id] = script; - sScriptMgr->IncrementScriptCount(); - - #ifdef SCRIPTS - UnusedScriptNamesContainer::iterator itr = std::lower_bound(UnusedScriptNames.begin(), UnusedScriptNames.end(), script->GetName()); - if (itr != UnusedScriptNames.end() && *itr == script->GetName()) - UnusedScriptNames.erase(itr); - #endif + // ... It exists. + existing = true; + break; } - else - { - // If the script is already assigned -> delete it! - TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, so the script can't work.", - script->GetName().c_str()); + } - ABORT(); // Error that should be fixed ASAP. - } + // If the script isn't assigned -> assign it! + if (!existing) + { + ScriptPointerList[id] = script; + sScriptMgr->IncrementScriptCount(); + + #ifdef SCRIPTS + UnusedScriptNamesContainer::iterator itr = std::lower_bound(UnusedScriptNames.begin(), UnusedScriptNames.end(), script->GetName()); + if (itr != UnusedScriptNames.end() && *itr == script->GetName()) + UnusedScriptNames.erase(itr); + #endif } else { - // The script uses a script name from database, but isn't assigned to anything. - TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.", script->GetName().c_str()); + // If the script is already assigned -> delete it! + TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, so the script can't work.", + script->GetName().c_str()); - // Avoid calling "delete script;" because we are currently in the script constructor - // In a valid scenario this will not happen because every script has a name assigned in the database - UnusedScripts.push_back(script); - return; + ABORT(); // Error that should be fixed ASAP. } } else { - // We're dealing with a code-only script; just add it. - ScriptPointerList[_scriptIdCounter++] = script; - sScriptMgr->IncrementScriptCount(); + // The script uses a script name from database, but isn't assigned to anything. + TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.", script->GetName().c_str()); + + // Avoid calling "delete script;" because we are currently in the script constructor + // In a valid scenario this will not happen because every script has a name assigned in the database + UnusedScripts.push_back(script); + return; } } - // Gets a script by its ID (assigned by ObjectMgr). - static TScript* GetScriptById(uint32 id) + // Adds a non database bound script + static void AddScript(std::false_type, TScript* const script) { - ScriptMapIterator it = ScriptPointerList.find(id); - if (it != ScriptPointerList.end()) - return it->second; - - return NULL; + // We're dealing with a code-only script; just add it. + ScriptPointerList[_scriptIdCounter++] = script; + sScriptMgr->IncrementScriptCount(); } - private: - // Counter used for code-only scripts. static uint32 _scriptIdCounter; }; diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 68a37163695..fc84d3d2275 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -152,10 +152,6 @@ class ScriptObject public: - // Do not override this in scripts; it should be overridden by the various script type classes. It indicates - // whether or not this script type must be assigned in the database. - virtual bool IsDatabaseBound() const { return false; } - const std::string& GetName() const { return _name; } protected: @@ -197,8 +193,6 @@ class SpellScriptLoader : public ScriptObject public: - bool IsDatabaseBound() const final override { return true; } - // Should return a fully valid SpellScript pointer. virtual SpellScript* GetSpellScript() const { return NULL; } @@ -355,8 +349,6 @@ class InstanceMapScript : public ScriptObject, public MapScript<InstanceMap> public: - bool IsDatabaseBound() const final override { return true; } - // Gets an InstanceScript object for this instance. virtual InstanceScript* GetInstanceScript(InstanceMap* /*map*/) const { return NULL; } }; @@ -376,8 +368,6 @@ class ItemScript : public ScriptObject public: - bool IsDatabaseBound() const final override { return true; } - // Called when a dummy spell effect is triggered on the item. virtual bool OnDummyEffect(Unit* /*caster*/, uint32 /*spellId*/, SpellEffIndex /*effIndex*/, Item* /*target*/) { return false; } @@ -425,8 +415,6 @@ class CreatureScript : public UnitScript, public UpdatableScript<Creature> public: - bool IsDatabaseBound() const final override { return true; } - // Called when a dummy spell effect is triggered on the creature. virtual bool OnDummyEffect(Unit* /*caster*/, uint32 /*spellId*/, SpellEffIndex /*effIndex*/, Creature* /*target*/) { return false; } @@ -463,8 +451,6 @@ class GameObjectScript : public ScriptObject, public UpdatableScript<GameObject> public: - bool IsDatabaseBound() const final override { return true; } - // Called when a dummy spell effect is triggered on the gameobject. virtual bool OnDummyEffect(Unit* /*caster*/, uint32 /*spellId*/, SpellEffIndex /*effIndex*/, GameObject* /*target*/) { return false; } @@ -510,8 +496,6 @@ class AreaTriggerScript : public ScriptObject public: - bool IsDatabaseBound() const final override { return true; } - // Called when the area trigger is activated by a player. virtual bool OnTrigger(Player* /*player*/, AreaTriggerEntry const* /*trigger*/, bool /*entered*/) { return false; } }; @@ -524,8 +508,6 @@ class BattlegroundScript : public ScriptObject public: - bool IsDatabaseBound() const final override { return true; } - // Should return a fully valid Battleground object for the type ID. virtual Battleground* GetBattleground() const = 0; }; @@ -538,8 +520,6 @@ class OutdoorPvPScript : public ScriptObject public: - bool IsDatabaseBound() const final override { return true; } - // Should return a fully valid OutdoorPvP object for the type ID. virtual OutdoorPvP* GetOutdoorPvP() const = 0; }; @@ -564,8 +544,6 @@ class WeatherScript : public ScriptObject, public UpdatableScript<Weather> public: - bool IsDatabaseBound() const final override { return true; } - // Called when the weather changes in the zone this script is associated with. virtual void OnChange(Weather* /*weather*/, WeatherState /*state*/, float /*grade*/) { } }; @@ -599,8 +577,6 @@ class ConditionScript : public ScriptObject public: - bool IsDatabaseBound() const final override { return true; } - // Called when a single condition is checked for a player. virtual bool OnConditionCheck(Condition const* /*condition*/, ConditionSourceInfo& /*sourceInfo*/) { return true; } }; @@ -647,8 +623,6 @@ class TransportScript : public ScriptObject, public UpdatableScript<Transport> public: - bool IsDatabaseBound() const final override { return true; } - // Called when a player boards the transport. virtual void OnAddPassenger(Transport* /*transport*/, Player* /*player*/) { } @@ -670,8 +644,6 @@ class AchievementCriteriaScript : public ScriptObject public: - bool IsDatabaseBound() const final override { return true; } - // Called when an additional criteria is checked. virtual bool OnCheck(Player* source, Unit* target) = 0; }; @@ -808,8 +780,6 @@ class GuildScript : public ScriptObject public: - bool IsDatabaseBound() const final override { return false; } - // Called when a member is added to the guild. virtual void OnAddMember(Guild* /*guild*/, Player* /*player*/, uint8& /*plRank*/) { } @@ -851,8 +821,6 @@ class GroupScript : public ScriptObject public: - bool IsDatabaseBound() const final override { return false; } - // Called when a member is added to a group. virtual void OnAddMember(Group* /*group*/, ObjectGuid /*guid*/) { } diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 2189fbffeff..f0126cd2782 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1411,6 +1411,7 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co case CMSG_STAND_STATE_CHANGE: // not profiled case CMSG_RANDOM_ROLL: // not profiled case CMSG_TIME_SYNC_RESPONSE: // not profiled + case CMSG_MOVE_FORCE_RUN_SPEED_CHANGE_ACK: // not profiled { // "0" is a magic number meaning there's no limit for the opcode. // All the opcodes above must cause little CPU usage and no sync/async database queries at all diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 6839e5831de..030ca1966f8 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -533,11 +533,11 @@ struct AccountInfo uint32 Id; bool IsLockedToIP; std::string LastIP; + std::string LockCountry; LocaleConstant Locale; std::string OS; bool IsBanned; - std::string LockCountry; } BattleNet; struct @@ -556,9 +556,9 @@ struct AccountInfo explicit AccountInfo(Field* fields) { - // 0 1 2 3 4 5 6 7 8 9 10 - // SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, - // 11 12 13 + // 0 1 2 3 4 5 6 7 8 9 10 11 + // SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, + // 12 13 14 // bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id // FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) // LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account r ON a.id = r.recruiter @@ -567,16 +567,17 @@ struct AccountInfo Game.SessionKey.SetHexStr(fields[1].GetCString()); BattleNet.LastIP = fields[2].GetString(); BattleNet.IsLockedToIP = fields[3].GetBool(); - Game.Expansion = fields[4].GetUInt8(); - Game.MuteTime = fields[5].GetInt64(); - BattleNet.Locale = LocaleConstant(fields[6].GetUInt8()); - Game.Recruiter = fields[7].GetUInt32(); - BattleNet.OS = fields[8].GetString(); - BattleNet.Id = fields[9].GetUInt32(); - Game.Security = AccountTypes(fields[10].GetUInt8()); - BattleNet.IsBanned = fields[11].GetUInt64() != 0; - Game.IsBanned = fields[12].GetUInt64() != 0; - Game.IsRectuiter = fields[13].GetUInt32() != 0; + BattleNet.LockCountry = fields[4].GetString(); + Game.Expansion = fields[5].GetUInt8(); + Game.MuteTime = fields[6].GetInt64(); + BattleNet.Locale = LocaleConstant(fields[7].GetUInt8()); + Game.Recruiter = fields[8].GetUInt32(); + BattleNet.OS = fields[9].GetString(); + BattleNet.Id = fields[10].GetUInt32(); + Game.Security = AccountTypes(fields[11].GetUInt8()); + BattleNet.IsBanned = fields[12].GetUInt64() != 0; + Game.IsBanned = fields[13].GetUInt64() != 0; + Game.IsRectuiter = fields[14].GetUInt32() != 0; uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION); if (Game.Expansion > world_expansion) @@ -698,7 +699,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth:: return; } } - else if (!account.BattleNet.LockCountry.empty() && !_ipCountry.empty()) + else if (!account.BattleNet.LockCountry.empty() && account.BattleNet.LockCountry != "00" && !_ipCountry.empty()) { if (account.BattleNet.LockCountry != _ipCountry) { diff --git a/src/server/game/Skills/SkillExtraItems.cpp b/src/server/game/Skills/SkillExtraItems.cpp index 8df9ce86f9a..2c9a2a7bcfd 100644 --- a/src/server/game/Skills/SkillExtraItems.cpp +++ b/src/server/game/Skills/SkillExtraItems.cpp @@ -20,11 +20,98 @@ #include "DatabaseEnv.h" #include "Log.h" #include "Player.h" +#include "ObjectMgr.h" #include <map> // some type definitions // no use putting them in the header file, they're only used in this .cpp +// struct to store information about perfection procs +// one entry per spell +struct SkillPerfectItemEntry +{ + // the spell id of the spell required - it's named "specialization" to conform with SkillExtraItemEntry + uint32 requiredSpecialization; + // perfection proc chance + float perfectCreateChance; + // itemid of the resulting perfect item + uint32 perfectItemType; + + SkillPerfectItemEntry() + : requiredSpecialization(0), perfectCreateChance(0.0f), perfectItemType(0) { } + SkillPerfectItemEntry(uint32 rS, float pCC, uint32 pIT) + : requiredSpecialization(rS), perfectCreateChance(pCC), perfectItemType(pIT) { } +}; + +// map to store perfection info. key = spellId of the creation spell, value is the perfectitementry as specified above +typedef std::map<uint32, SkillPerfectItemEntry> SkillPerfectItemMap; + +SkillPerfectItemMap SkillPerfectItemStore; + +// loads the perfection proc info from DB +void LoadSkillPerfectItemTable() +{ + uint32 oldMSTime = getMSTime(); + + SkillPerfectItemStore.clear(); // reload capability + + // 0 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT spellId, requiredSpecialization, perfectCreateChance, perfectItemType FROM skill_perfect_item_template"); + + if (!result) + { + TC_LOG_ERROR("server.loading", ">> Loaded 0 spell perfection definitions. DB table `skill_perfect_item_template` is empty."); + return; + } + + uint32 count = 0; + + do /* fetch data and run sanity checks */ + { + Field* fields = result->Fetch(); + + uint32 spellId = fields[0].GetUInt32(); + + if (!sSpellMgr->GetSpellInfo(spellId)) + { + TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u has non-existent spell id in `skill_perfect_item_template`!", spellId); + continue; + } + + uint32 requiredSpecialization = fields[1].GetUInt32(); + if (!sSpellMgr->GetSpellInfo(requiredSpecialization)) + { + TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u has non-existent required specialization spell id %u in `skill_perfect_item_template`!", spellId, requiredSpecialization); + continue; + } + + float perfectCreateChance = fields[2].GetFloat(); + if (perfectCreateChance <= 0.0f) + { + TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u has impossibly low proc chance in `skill_perfect_item_template`!", spellId); + continue; + } + + uint32 perfectItemType = fields[3].GetUInt32(); + if (!sObjectMgr->GetItemTemplate(perfectItemType)) + { + TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u references non-existent perfect item id %u in `skill_perfect_item_template`!", spellId, perfectItemType); + continue; + } + + SkillPerfectItemEntry& skillPerfectItemEntry = SkillPerfectItemStore[spellId]; + + skillPerfectItemEntry.requiredSpecialization = requiredSpecialization; + skillPerfectItemEntry.perfectCreateChance = perfectCreateChance; + skillPerfectItemEntry.perfectItemType = perfectItemType; + + ++count; + } + while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u spell perfection definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + // struct to store information about extra item creation // one entry for every spell that is able to create an extra item struct SkillExtraItemEntry @@ -112,6 +199,30 @@ void LoadSkillExtraItemTable() TC_LOG_INFO("server.loading", ">> Loaded %u spell specialization definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +bool CanCreatePerfectItem(Player* player, uint32 spellId, float &perfectCreateChance, uint32 &perfectItemType) +{ + SkillPerfectItemMap::const_iterator ret = SkillPerfectItemStore.find(spellId); + // no entry in DB means no perfection proc possible + if (ret == SkillPerfectItemStore.end()) + return false; + + SkillPerfectItemEntry const* thisEntry = &ret->second; + // lack of entry means no perfection proc possible + if (!thisEntry) + return false; + + // if you don't have the spell needed, then no procs for you + if (!player->HasSpell(thisEntry->requiredSpecialization)) + return false; + + // set values as appropriate + perfectCreateChance = thisEntry->perfectCreateChance; + perfectItemType = thisEntry->perfectItemType; + + // and tell the caller to start rolling the dice + return true; +} + bool CanCreateExtraItems(Player* player, uint32 spellId, float &additionalChance, uint8 &additionalMax) { // get the info for the specified spell diff --git a/src/server/game/Skills/SkillExtraItems.h b/src/server/game/Skills/SkillExtraItems.h index 0cdfff74ed2..118c49ed00f 100644 --- a/src/server/game/Skills/SkillExtraItems.h +++ b/src/server/game/Skills/SkillExtraItems.h @@ -23,6 +23,10 @@ // predef classes used in functions class Player; +// returns true and sets the appropriate info if the player can create a perfect item with the given spellId +bool CanCreatePerfectItem(Player* player, uint32 spellId, float &perfectCreateChance, uint32 &perfectItemType); +// load perfection proc info from DB +void LoadSkillPerfectItemTable(); // returns true and sets the appropriate info if the player can create extra items with the given spellId bool CanCreateExtraItems(Player* player, uint32 spellId, float &additionalChance, uint8 &additionalMax); // function to load the extra item creation info from DB diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 77f3d2b905e..3431eb71898 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1456,6 +1456,22 @@ void Spell::DoCreateItem(uint32 /*i*/, uint32 itemtype, std::vector<int32> const if (num_to_add > pProto->GetMaxStackSize()) num_to_add = pProto->GetMaxStackSize(); + /* == gem perfection handling == */ + + // the chance of getting a perfect result + float perfectCreateChance = 0.0f; + // the resulting perfect item if successful + uint32 perfectItemType = itemtype; + // get perfection capability and chance + if (CanCreatePerfectItem(player, m_spellInfo->Id, perfectCreateChance, perfectItemType)) + if (roll_chance_f(perfectCreateChance)) // if the roll succeeds... + newitemid = perfectItemType; // the perfect item replaces the regular one + + /* == gem perfection handling over == */ + + + /* == profession specialization handling == */ + // init items_count to 1, since 1 item will be created regardless of specialization int items_count=1; // the chance to create additional items @@ -1464,15 +1480,16 @@ void Spell::DoCreateItem(uint32 /*i*/, uint32 itemtype, std::vector<int32> const uint8 additionalMaxNum=0; // get the chance and maximum number for creating extra items if (CanCreateExtraItems(player, m_spellInfo->Id, additionalCreateChance, additionalMaxNum)) - { // roll with this chance till we roll not to create or we create the max num while (roll_chance_f(additionalCreateChance) && items_count <= additionalMaxNum) ++items_count; - } // really will be created more items num_to_add *= items_count; + /* == profession specialization handling over == */ + + // can the player store the new item? ItemPosCountVec dest; uint32 no_space = 0; @@ -4114,11 +4131,14 @@ void Spell::EffectEnchantHeldItem(SpellEffIndex /*effIndex*/) if (effectInfo->MiscValue) { uint32 enchant_id = effectInfo->MiscValue; - int32 duration = m_spellInfo->GetDuration(); //Try duration index first .. + int32 duration = m_spellInfo->GetDuration(); //Try duration index first .. if (!duration) - duration = damage;//+1; //Base points after .. + duration = damage;//+1; //Base points after .. if (!duration) - duration = 10; //10 seconds for enchants which don't have listed duration + duration = 10 * IN_MILLISECONDS; //10 seconds for enchants which don't have listed duration + + if (m_spellInfo->Id == 14792) // Venomhide Poison + duration = 5 * MINUTE * IN_MILLISECONDS; SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); if (!pEnchant) @@ -4132,7 +4152,7 @@ void Spell::EffectEnchantHeldItem(SpellEffIndex /*effIndex*/) return; // Apply the temporary enchantment - item->SetEnchantment(slot, enchant_id, duration*IN_MILLISECONDS, 0, m_caster->GetGUID()); + item->SetEnchantment(slot, enchant_id, duration, 0, m_caster->GetGUID()); item_owner->ApplyEnchantment(item, slot, true); } } diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index 3b86e3d594b..c2d1356e61a 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -870,6 +870,60 @@ void SpellHistory::SendClearCooldowns(std::vector<int32> const& cooldowns) const } } +void SpellHistory::SaveCooldownStateBeforeDuel() +{ + _spellCooldownsBeforeDuel = _spellCooldowns; +} + +void SpellHistory::RestoreCooldownStateAfterDuel() +{ + if (Player* player = _owner->ToPlayer()) + { + // add all profession CDs created while in duel (if any) + for (auto const& c : _spellCooldowns) + { + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(c.first); + + if (spellInfo->RecoveryTime > 10 * MINUTE * IN_MILLISECONDS || + spellInfo->CategoryRecoveryTime > 10 * MINUTE * IN_MILLISECONDS) + _spellCooldownsBeforeDuel[c.first] = _spellCooldowns[c.first]; + } + + _spellCooldowns = _spellCooldownsBeforeDuel; + + // update the client: clear all cooldowns + std::vector<int32> resetCooldowns; + resetCooldowns.reserve(_spellCooldowns.size()); + + for (auto const& c : _spellCooldowns) + resetCooldowns.push_back(c.first); + + if (resetCooldowns.empty()) + return; + + SendClearCooldowns(resetCooldowns); + + // update the client: restore old cooldowns + WorldPackets::Spells::SpellCooldown spellCooldown; + spellCooldown.Caster = _owner->GetGUID(); + spellCooldown.Flags = SPELL_COOLDOWN_FLAG_NONE; + + for (auto const& c : _spellCooldowns) + { + Clock::time_point now = Clock::now(); + uint32 cooldownDuration = c.second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(c.second.CooldownEnd - now).count() : 0; + + // cooldownDuration must be between 0 and 10 minutes in order to avoid any visual bugs + if (cooldownDuration == 0 || cooldownDuration > 10 * MINUTE * IN_MILLISECONDS) + continue; + + spellCooldown.SpellCooldowns.emplace_back(c.first, cooldownDuration); + } + + player->SendDirectMessage(spellCooldown.Write()); + } +} + template void SpellHistory::LoadFromDB<Player>(PreparedQueryResult cooldownsResult, PreparedQueryResult chargesResult); template void SpellHistory::LoadFromDB<Pet>(PreparedQueryResult cooldownsResult, PreparedQueryResult chargesResult); template void SpellHistory::SaveToDB<Player>(SQLTransaction& trans); diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h index 0a504668997..1a6fc205f96 100644 --- a/src/server/game/Spells/SpellHistory.h +++ b/src/server/game/Spells/SpellHistory.h @@ -144,6 +144,10 @@ public: void AddGlobalCooldown(SpellInfo const* spellInfo, uint32 duration); void CancelGlobalCooldown(SpellInfo const* spellInfo); + uint16 GetArenaCooldownsSize(); + void SaveCooldownStateBeforeDuel(); + void RestoreCooldownStateAfterDuel(); + private: Player* GetPlayerOwner() const; void SendClearCooldowns(std::vector<int32> const& cooldowns) const; @@ -155,6 +159,7 @@ private: Unit* _owner; CooldownStorageType _spellCooldowns; + CooldownStorageType _spellCooldownsBeforeDuel; CategoryCooldownStorageType _categoryCooldowns; Clock::time_point _schoolLockouts[MAX_SPELL_SCHOOL]; ChargeStorageType _categoryCharges; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 9d6466dfab7..75f419dc99e 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3228,6 +3228,19 @@ void SpellMgr::LoadSpellInfoCorrections() //! HACK: This spell break quest complete for alliance and on retail not used °_O const_cast<SpellEffectInfo*>(spellInfo->GetEffect(EFFECT_0))->Effect = 0; break; + // VIOLET HOLD SPELLS + // + case 54258: // Water Globule (Ichoron) + case 54264: // Water Globule (Ichoron) + case 54265: // Water Globule (Ichoron) + case 54266: // Water Globule (Ichoron) + case 54267: // Water Globule (Ichoron) + // in 3.3.5 there is only one radius in dbc which is 0 yards in this case + // use max radius from 4.3.4 + const_cast<SpellEffectInfo*>(spellInfo->GetEffect(EFFECT_0))->RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_25_YARDS); + break; + // ENDOF VIOLET HOLD + // // ULDUAR SPELLS // case 62374: // Pursued (Flame Leviathan) diff --git a/src/server/game/Weather/WeatherMgr.cpp b/src/server/game/Weather/WeatherMgr.cpp index 8ab122a82bb..f80e2977b1e 100644 --- a/src/server/game/Weather/WeatherMgr.cpp +++ b/src/server/game/Weather/WeatherMgr.cpp @@ -132,7 +132,7 @@ void LoadWeatherData() } } - wzc.ScriptId = sObjectMgr->GetScriptId(fields[13].GetCString()); + wzc.ScriptId = sObjectMgr->GetScriptId(fields[13].GetString()); ++count; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 872f3bb160a..fa0b67082d2 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1278,6 +1278,7 @@ void World::LoadConfigSettings(bool reload) if (m_bool_configs[CONFIG_START_ALL_SPELLS]) TC_LOG_WARN("server.loading", "PlayerStart.AllSpells enabled - may not function as intended!"); m_int_configs[CONFIG_HONOR_AFTER_DUEL] = sConfigMgr->GetIntDefault("HonorPointsAfterDuel", 0); + m_bool_configs[CONFIG_RESET_DUEL_COOLDOWNS] = sConfigMgr->GetBoolDefault("ResetDuelCooldowns", false); m_bool_configs[CONFIG_START_ALL_EXPLORED] = sConfigMgr->GetBoolDefault("PlayerStart.MapsExplored", false); m_bool_configs[CONFIG_START_ALL_REP] = sConfigMgr->GetBoolDefault("PlayerStart.AllReputation", false); m_bool_configs[CONFIG_ALWAYS_MAXSKILL] = sConfigMgr->GetBoolDefault("AlwaysMaxWeaponSkill", false); @@ -1750,6 +1751,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Skill Extra Item Table..."); LoadSkillExtraItemTable(); + TC_LOG_INFO("server.loading", "Loading Skill Perfection Data Table..."); + LoadSkillPerfectItemTable(); + TC_LOG_INFO("server.loading", "Loading Skill Fishing base level requirements..."); sObjectMgr->LoadFishingBaseSkillLevel(); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index f7b61452fda..153a7730bef 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -176,6 +176,7 @@ enum WorldBoolConfigs CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA, CONFIG_FEATURE_SYSTEM_BPAY_STORE_ENABLED, CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_ENABLED, + CONFIG_RESET_DUEL_COOLDOWNS, BOOL_CONFIG_VALUE_COUNT }; diff --git a/src/server/scripts/Commands/cs_cheat.cpp b/src/server/scripts/Commands/cs_cheat.cpp index fda2f885cd5..6262d380075 100644 --- a/src/server/scripts/Commands/cs_cheat.cpp +++ b/src/server/scripts/Commands/cs_cheat.cpp @@ -24,7 +24,6 @@ EndScriptData */ #include "Chat.h" #include "Language.h" -#include "ObjectMgr.h" #include "Player.h" #include "ScriptMgr.h" @@ -163,8 +162,8 @@ public: { Player* player = handler->GetSession()->GetPlayer(); - const char* enabled = "enabled"; - const char* disabled = "disabled"; + const char* enabled = "ON"; + const char* disabled = "OFF"; handler->SendSysMessage(LANG_COMMAND_CHEAT_STATUS); handler->PSendSysMessage(LANG_COMMAND_CHEAT_GOD, player->GetCommandStatus(CHEAT_GOD) ? enabled : disabled); @@ -172,6 +171,7 @@ public: handler->PSendSysMessage(LANG_COMMAND_CHEAT_CT, player->GetCommandStatus(CHEAT_CASTTIME) ? enabled : disabled); handler->PSendSysMessage(LANG_COMMAND_CHEAT_POWER, player->GetCommandStatus(CHEAT_POWER) ? enabled : disabled); handler->PSendSysMessage(LANG_COMMAND_CHEAT_WW, player->GetCommandStatus(CHEAT_WATERWALK) ? enabled : disabled); + handler->PSendSysMessage(LANG_COMMAND_CHEAT_TAXINODES, player->isTaxiCheater() ? enabled : disabled); return true; } @@ -184,13 +184,7 @@ public: Player* target = handler->GetSession()->GetPlayer(); if (!*args) - { argstr = (target->GetCommandStatus(CHEAT_WATERWALK)) ? "off" : "on"; - if (target->GetCommandStatus(CHEAT_WATERWALK)) - argstr = "off"; - else - argstr = "on"; - } if (argstr == "off") { @@ -212,15 +206,8 @@ public: static bool HandleTaxiCheatCommand(ChatHandler* handler, const char* args) { - if (!*args) - { - handler->SendSysMessage(LANG_USE_BOL); - handler->SetSentErrorMessage(true); - return false; - } std::string argstr = (char*)args; - Player* chr = handler->getSelectedPlayer(); if (!chr) @@ -228,24 +215,28 @@ public: else if (handler->HasLowerSecurity(chr, ObjectGuid::Empty)) // check online security return false; - if (argstr == "on") - { - chr->SetTaxiCheater(true); - handler->PSendSysMessage(LANG_YOU_GIVE_TAXIS, handler->GetNameLink(chr).c_str()); - if (handler->needReportToTarget(chr)) - ChatHandler(chr->GetSession()).PSendSysMessage(LANG_YOURS_TAXIS_ADDED, handler->GetNameLink().c_str()); - return true; - } + if (!*args) + argstr = (chr->isTaxiCheater()) ? "off" : "on"; + if (argstr == "off") + { chr->SetTaxiCheater(false); handler->PSendSysMessage(LANG_YOU_REMOVE_TAXIS, handler->GetNameLink(chr).c_str()); if (handler->needReportToTarget(chr)) ChatHandler(chr->GetSession()).PSendSysMessage(LANG_YOURS_TAXIS_REMOVED, handler->GetNameLink().c_str()); - return true; } + else if (argstr == "on") + { + chr->SetTaxiCheater(true); + handler->PSendSysMessage(LANG_YOU_GIVE_TAXIS, handler->GetNameLink(chr).c_str()); + if (handler->needReportToTarget(chr)) + ChatHandler(chr->GetSession()).PSendSysMessage(LANG_YOURS_TAXIS_ADDED, handler->GetNameLink().c_str()); + return true; + } + handler->SendSysMessage(LANG_USE_BOL); handler->SetSentErrorMessage(true); @@ -257,10 +248,11 @@ public: if (!*args) return false; + // std::int flag = (char*)args; int flag = atoi((char*)args); Player* chr = handler->getSelectedPlayer(); - if (chr == NULL) + if (!chr) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); handler->SetSentErrorMessage(true); diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index b3a1f0317c1..07c9af94bf4 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -33,6 +33,7 @@ EndScriptData */ #include "GossipDef.h" #include "Transport.h" #include "Language.h" +#include "MapManager.h" #include "MovementPackets.h" #include "SpellPackets.h" #include "ScenePackets.h" @@ -95,6 +96,7 @@ public: { "los", rbac::RBAC_PERM_COMMAND_DEBUG_LOS, false, &HandleDebugLoSCommand, "" }, { "moveflags", rbac::RBAC_PERM_COMMAND_DEBUG_MOVEFLAGS, false, &HandleDebugMoveflagsCommand, "" }, { "transport", rbac::RBAC_PERM_COMMAND_DEBUG_TRANSPORT, false, &HandleDebugTransportCommand, "" }, + { "loadcells", rbac::RBAC_PERM_COMMAND_DEBUG_LOADCELLS, false, &HandleDebugLoadCellsCommand, "",}, { "phase", rbac::RBAC_PERM_COMMAND_DEBUG_PHASE, false, &HandleDebugPhaseCommand, "" }, }; static std::vector<ChatCommand> commandTable = @@ -1393,6 +1395,30 @@ public: return true; } + static bool HandleDebugLoadCellsCommand(ChatHandler* handler, char const* args) + { + Player* player = handler->GetSession()->GetPlayer(); + if (!player) + return false; + + Map* map = nullptr; + + if (*args) + { + int32 mapId = atoi(args); + map = sMapMgr->FindBaseNonInstanceMap(mapId); + } + if (!map) + map = player->GetMap(); + + for (uint32 cellX = 0; cellX < TOTAL_NUMBER_OF_CELLS_PER_MAP; cellX++) + for (uint32 cellY = 0; cellY < TOTAL_NUMBER_OF_CELLS_PER_MAP; cellY++) + map->LoadGrid((cellX + 0.5f - CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL, (cellY + 0.5f - CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL); + + handler->PSendSysMessage("Cells loaded (mapId: %u)", map->GetId()); + return true; + } + static bool HandleDebugPhaseCommand(ChatHandler* handler, char const* /*args*/) { Unit* target = handler->getSelectedUnit(); diff --git a/src/server/scripts/Commands/cs_instance.cpp b/src/server/scripts/Commands/cs_instance.cpp index 347252ce90d..4ebc257b58b 100644 --- a/src/server/scripts/Commands/cs_instance.cpp +++ b/src/server/scripts/Commands/cs_instance.cpp @@ -252,7 +252,8 @@ public: } map->GetInstanceScript()->SetBossState(encounterId, EncounterState(state)); - handler->PSendSysMessage(LANG_COMMAND_INST_SET_BOSS_STATE, encounterId, state); + std::string stateName = InstanceScript::GetBossStateName(state); + handler->PSendSysMessage(LANG_COMMAND_INST_SET_BOSS_STATE, encounterId, state, stateName); return true; } @@ -316,7 +317,8 @@ public: } uint32 state = map->GetInstanceScript()->GetBossState(encounterId); - handler->PSendSysMessage(LANG_COMMAND_INST_GET_BOSS_STATE, encounterId, state); + std::string stateName = InstanceScript::GetBossStateName(state); + handler->PSendSysMessage(LANG_COMMAND_INST_GET_BOSS_STATE, encounterId, state, stateName); return true; } }; diff --git a/src/server/scripts/Commands/cs_lfg.cpp b/src/server/scripts/Commands/cs_lfg.cpp index 6939d2c4946..92bc7c5f543 100644 --- a/src/server/scripts/Commands/cs_lfg.cpp +++ b/src/server/scripts/Commands/cs_lfg.cpp @@ -19,7 +19,9 @@ #include "Chat.h" #include "Language.h" #include "LFGMgr.h" +#include "ObjectMgr.h" #include "Group.h" +#include "GroupMgr.h" #include "Player.h" void GetPlayerInfo(ChatHandler* handler, Player* player) @@ -72,25 +74,55 @@ public: static bool HandleLfgGroupInfoCommand(ChatHandler* handler, char const* args) { - Player* target = NULL; - std::string playerName; - if (!handler->extractPlayerTarget((char*)args, &target, NULL, &playerName)) + Player* playerTarget; + ObjectGuid guidTarget; + std::string nameTarget; + + ObjectGuid parseGUID = ObjectGuid::Create<HighGuid::Player>(uint64(atoull(args))); + + if (sObjectMgr->GetPlayerNameByGUID(parseGUID, nameTarget)) + { + playerTarget = ObjectAccessor::FindPlayer(parseGUID); + guidTarget = parseGUID; + } + else if (!handler->extractPlayerTarget((char*)args, &playerTarget, &guidTarget, &nameTarget)) return false; - Group* grp = target->GetGroup(); - if (!grp) + Group* groupTarget = NULL; + + if (playerTarget) + groupTarget = playerTarget->GetGroup(); + else + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GROUP_MEMBER); + stmt->setUInt32(0, guidTarget.GetCounter()); + PreparedQueryResult resultGroup = CharacterDatabase.Query(stmt); + if (resultGroup) + groupTarget = sGroupMgr->GetGroupByDbStoreId((*resultGroup)[0].GetUInt32()); + } + if (!groupTarget) { - handler->PSendSysMessage(LANG_LFG_NOT_IN_GROUP, playerName.c_str()); - return true; + handler->PSendSysMessage(LANG_LFG_NOT_IN_GROUP, nameTarget.c_str()); + handler->SetSentErrorMessage(true); + return false; } - ObjectGuid guid = grp->GetGUID(); + ObjectGuid guid = groupTarget->GetGUID(); std::string const& state = lfg::GetStateString(sLFGMgr->GetState(guid)); - handler->PSendSysMessage(LANG_LFG_GROUP_INFO, grp->isLFGGroup(), + handler->PSendSysMessage(LANG_LFG_GROUP_INFO, groupTarget->isLFGGroup(), state.c_str(), sLFGMgr->GetDungeon(guid)); - for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - GetPlayerInfo(handler, itr->GetSource()); + Group::MemberSlotList const& members = groupTarget->GetMemberSlots(); + + for (Group::MemberSlotList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + { + Group::MemberSlot const& slot = *itr; + Player* p = ObjectAccessor::FindPlayer((*itr).guid); + if (p) + GetPlayerInfo(handler, p); + else + handler->PSendSysMessage("%s is offline.", slot.name.c_str()); + } return true; } diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp index e63e9e98527..674b6dd74ee 100644 --- a/src/server/scripts/Commands/cs_modify.cpp +++ b/src/server/scripts/Commands/cs_modify.cpp @@ -133,7 +133,7 @@ public: return false; } - Player* target = handler->getSelectedPlayer(); + Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -161,17 +161,6 @@ public: if (!*args) return false; - // char* pmana = strtok((char*)args, " "); - // if (!pmana) - // return false; - - // char* pmanaMax = strtok(NULL, " "); - // if (!pmanaMax) - // return false; - - // int32 manam = atoi(pmanaMax); - // int32 mana = atoi(pmana); - int32 energy = atoi((char*)args)*10; int32 energym = atoi((char*)args)*10; @@ -182,7 +171,7 @@ public: return false; } - Player* target = handler->getSelectedPlayer(); + Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -212,17 +201,6 @@ public: if (!*args) return false; - // char* pmana = strtok((char*)args, " "); - // if (!pmana) - // return false; - - // char* pmanaMax = strtok(NULL, " "); - // if (!pmanaMax) - // return false; - - // int32 manam = atoi(pmanaMax); - // int32 mana = atoi(pmana); - int32 rage = atoi((char*)args)*10; int32 ragem = atoi((char*)args)*10; @@ -233,7 +211,7 @@ public: return false; } - Player* target = handler->getSelectedPlayer(); + Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -271,7 +249,7 @@ public: return false; } - Player* target = handler->getSelectedPlayer(); + Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -387,7 +365,7 @@ public: else mark = atoi(pmark); - Player* target = handler->getSelectedPlayer(); + Player* target = handler->getSelectedPlayerOrSelf(); if (target == NULL) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -946,7 +924,7 @@ public: return false; } - Player* target = handler->getSelectedPlayer(); + Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -987,7 +965,7 @@ public: if (!*args) return false; - Player* target = handler->getSelectedPlayer(); + Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -1112,7 +1090,7 @@ public: if (!*args) return false; - Player* target = handler->getSelectedPlayer(); + Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); @@ -1142,7 +1120,7 @@ public: if (drunklevel > 100) drunklevel = 100; - if (Player* target = handler->getSelectedPlayer()) + if (Player* target = handler->getSelectedPlayerOrSelf()) target->SetDrunkValue(drunklevel); return true; @@ -1153,7 +1131,7 @@ public: if (!*args) return false; - Player* target = handler->getSelectedPlayer(); + Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); @@ -1306,7 +1284,7 @@ public: if (!*args) return false; - Player* target = handler->getSelectedPlayer(); + Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 99b6ff888a5..dfddcaf395e 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -752,12 +752,21 @@ public: return true; } - static bool HandleReloadSkillExtraItemTemplateCommand(ChatHandler* handler, const char* /*args*/) + static bool HandleReloadSkillPerfectItemTemplateCommand(ChatHandler* handler, const char* /*args*/) + { // latched onto HandleReloadSkillExtraItemTemplateCommand as it's part of that table group (and i don't want to chance all the command IDs) + TC_LOG_INFO("misc", "Re-Loading Skill Perfection Data Table..."); + LoadSkillPerfectItemTable(); + handler->SendGlobalGMSysMessage("DB table `skill_perfect_item_template` (perfect item procs when crafting) reloaded."); + return true; + } + + static bool HandleReloadSkillExtraItemTemplateCommand(ChatHandler* handler, const char* args) { TC_LOG_INFO("misc", "Re-Loading Skill Extra Item Table..."); LoadSkillExtraItemTable(); handler->SendGlobalGMSysMessage("DB table `skill_extra_item_template` (extra item creation when crafting) reloaded."); - return true; + + return HandleReloadSkillPerfectItemTemplateCommand(handler, args); } static bool HandleReloadSkillFishingBaseLevelCommand(ChatHandler* handler, const char* /*args*/) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_anubshiah.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_anubshiah.cpp deleted file mode 100644 index c2261785782..00000000000 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_anubshiah.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" - -enum Spells -{ - SPELL_SHADOWBOLT = 17228, - SPELL_CURSEOFTONGUES = 15470, - SPELL_CURSEOFWEAKNESS = 17227, - SPELL_DEMONARMOR = 11735, - SPELL_ENVELOPINGWEB = 15471 -}; - -enum Events -{ - EVENT_SHADOWBOLT = 1, - EVENT_CURSE_OF_TONGUES = 2, - EVENT_CURSE_OF_WEAKNESS = 3, - EVENT_DEMON_ARMOR = 4, - EVENT_ENVELOPING_WEB = 5 -}; - -class boss_anubshiah : public CreatureScript -{ - public: - boss_anubshiah() : CreatureScript("boss_anubshiah") { } - - struct boss_anubshiahAI : public ScriptedAI - { - boss_anubshiahAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override - { - _events.Reset(); - } - - void EnterCombat(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_SHADOWBOLT, 7000); - _events.ScheduleEvent(EVENT_CURSE_OF_TONGUES, 24000); - _events.ScheduleEvent(EVENT_CURSE_OF_WEAKNESS, 12000); - _events.ScheduleEvent(EVENT_DEMON_ARMOR, 3000); - _events.ScheduleEvent(EVENT_ENVELOPING_WEB, 16000); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SHADOWBOLT: - DoCast(me, SPELL_SHADOWBOLT); - _events.ScheduleEvent(EVENT_SHADOWBOLT, 7000); - break; - case EVENT_CURSE_OF_TONGUES: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_CURSEOFTONGUES); - _events.ScheduleEvent(EVENT_CURSE_OF_TONGUES, 18000); - break; - case EVENT_CURSE_OF_WEAKNESS: - DoCastVictim(SPELL_CURSEOFWEAKNESS); - _events.ScheduleEvent(EVENT_CURSE_OF_WEAKNESS, 45000); - break; - case EVENT_DEMON_ARMOR: - DoCast(me, SPELL_DEMONARMOR); - _events.ScheduleEvent(EVENT_DEMON_ARMOR, 300000); - break; - case EVENT_ENVELOPING_WEB: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_ENVELOPINGWEB); - _events.ScheduleEvent(EVENT_ENVELOPING_WEB, 12000); - break; - default: - break; - } - } - - DoMeleeAttackIfReady(); - } - - private: - EventMap _events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new boss_anubshiahAI(creature); - } -}; - -void AddSC_boss_anubshiah() -{ - new boss_anubshiah(); -} diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_gorosh_the_dervish.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_gorosh_the_dervish.cpp deleted file mode 100644 index 83702ffb16d..00000000000 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_gorosh_the_dervish.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" - -enum Spells -{ - SPELL_WHIRLWIND = 15589, - SPELL_MORTALSTRIKE = 24573 -}; - -enum Events -{ - EVENT_WHIRLWIND = 1, - EVENT_MORTALSTRIKE = 2 -}; - -class boss_gorosh_the_dervish : public CreatureScript -{ - public: - boss_gorosh_the_dervish() : CreatureScript("boss_gorosh_the_dervish") { } - - struct boss_gorosh_the_dervishAI : public ScriptedAI - { - boss_gorosh_the_dervishAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override - { - _events.Reset(); - } - - void EnterCombat(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_WHIRLWIND, 12000); - _events.ScheduleEvent(EVENT_MORTALSTRIKE, 22000); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_WHIRLWIND: - DoCast(me, SPELL_WHIRLWIND); - _events.ScheduleEvent(EVENT_WHIRLWIND, 15000); - break; - case EVENT_MORTALSTRIKE: - DoCastVictim(SPELL_MORTALSTRIKE); - _events.ScheduleEvent(EVENT_MORTALSTRIKE, 15000); - break; - default: - break; - } - } - - DoMeleeAttackIfReady(); - } - - private: - EventMap _events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new boss_gorosh_the_dervishAI(creature); - } -}; - -void AddSC_boss_gorosh_the_dervish() -{ - new boss_gorosh_the_dervish(); -} diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_grizzle.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_grizzle.cpp deleted file mode 100644 index 44dbbe3f4c2..00000000000 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_grizzle.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" - -enum Grizzle -{ - SPELL_GROUNDTREMOR = 6524, - SPELL_FRENZY = 28371, - EMOTE_FRENZY_KILL = 0 -}; - -enum Events -{ - EVENT_GROUNDTREMOR = 1, - EVENT_FRENZY = 2 -}; - -enum Phases -{ - PHASE_ONE = 1, - PHASE_TWO = 2 -}; - -class boss_grizzle : public CreatureScript -{ - public: - boss_grizzle() : CreatureScript("boss_grizzle") { } - - struct boss_grizzleAI : public ScriptedAI - { - boss_grizzleAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override - { - _events.Reset(); - } - - void EnterCombat(Unit* /*who*/) override - { - _events.SetPhase(PHASE_ONE); - _events.ScheduleEvent(EVENT_GROUNDTREMOR, 12000); - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage) override - { - if (me->HealthBelowPctDamaged(50, damage) && _events.IsInPhase(PHASE_ONE)) - { - _events.SetPhase(PHASE_TWO); - _events.ScheduleEvent(EVENT_FRENZY, 0, 0, PHASE_TWO); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_GROUNDTREMOR: - DoCastVictim(SPELL_GROUNDTREMOR); - _events.ScheduleEvent(EVENT_GROUNDTREMOR, 8000); - break; - case EVENT_FRENZY: - DoCast(me, SPELL_FRENZY); - Talk(EMOTE_FRENZY_KILL); - _events.ScheduleEvent(EVENT_FRENZY, 15000, 0, PHASE_TWO); - break; - default: - break; - } - } - - DoMeleeAttackIfReady(); - } - - private: - EventMap _events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new boss_grizzleAI(creature); - } -}; - -void AddSC_boss_grizzle() -{ - new boss_grizzle(); -} diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/blackwing_lair.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/blackwing_lair.h index c79a57d0746..12798833a3f 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/blackwing_lair.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/blackwing_lair.h @@ -25,14 +25,18 @@ uint32 const EncounterCount = 8; enum BWLEncounter { - BOSS_RAZORGORE = 0, - BOSS_VAELASTRAZ = 1, - BOSS_BROODLORD = 2, - BOSS_FIREMAW = 3, - BOSS_EBONROC = 4, - BOSS_FLAMEGOR = 5, - BOSS_CHROMAGGUS = 6, - BOSS_NEFARIAN = 7 + // Encounter States/Boss GUIDs + DATA_RAZORGORE_THE_UNTAMED = 0, + DATA_VAELASTRAZ_THE_CORRUPT = 1, + DATA_BROODLORD_LASHLAYER = 2, + DATA_FIREMAW = 3, + DATA_EBONROC = 4, + DATA_FLAMEGOR = 5, + DATA_CHROMAGGUS = 6, + DATA_NEFARIAN = 7, + + // Additional Data + DATA_LORD_VICTOR_NEFARIUS = 8 }; enum CreatureIds @@ -44,7 +48,7 @@ enum CreatureIds NPC_BLACKWING_WARLOCK = 12459, NPC_VAELASTRAZ = 13020, NPC_BROODLORD = 12017, - NPC_FIRENAW = 11983, + NPC_FIREMAW = 11983, NPC_EBONROC = 14601, NPC_FLAMEGOR = 11981, NPC_CHROMAGGUS = 14020, @@ -52,17 +56,14 @@ enum CreatureIds NPC_NEFARIAN = 11583 }; -enum BWLData64 +enum GameObjectIds { - DATA_RAZORGORE_THE_UNTAMED = 1, - DATA_VAELASTRAZ_THE_CORRUPT, - DATA_BROODLORD_LASHLAYER, - DATA_FIRENAW, - DATA_EBONROC, - DATA_FLAMEGOR, - DATA_CHROMAGGUS, - DATA_LORD_VICTOR_NEFARIUS, - DATA_NEFARIAN + GO_BLACK_DRAGON_EGG = 177807, + GO_BOSSGATE01 = 175946, + GO_DRAKE_RIDER_PORTCULLIS = 175185, + GO_ALTERAC_VALLEY_GATE = 180424, + GO_GATE = 185483, + GO_VACCUUM_EXIT_GATE = 181125 }; enum BWLEvents @@ -79,4 +80,4 @@ enum BWLMisc DATA_EGG_EVENT }; -#endif
\ No newline at end of file +#endif diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_broodlord_lashlayer.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_broodlord_lashlayer.cpp index 2609cf5fdf8..5ba933005ec 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_broodlord_lashlayer.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_broodlord_lashlayer.cpp @@ -50,16 +50,10 @@ public: struct boss_broodlordAI : public BossAI { - boss_broodlordAI(Creature* creature) : BossAI(creature, BOSS_BROODLORD) { } + boss_broodlordAI(Creature* creature) : BossAI(creature, DATA_BROODLORD_LASHLAYER) { } void EnterCombat(Unit* /*who*/) override { - if (instance->GetBossState(BOSS_VAELASTRAZ) != DONE) - { - EnterEvadeMode(); - return; - } - _EnterCombat(); Talk(SAY_AGGRO); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_chromaggus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_chromaggus.cpp index 61d6c052cdf..9a49b96e68e 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_chromaggus.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_chromaggus.cpp @@ -71,7 +71,7 @@ public: struct boss_chromaggusAI : public BossAI { - boss_chromaggusAI(Creature* creature) : BossAI(creature, BOSS_CHROMAGGUS) + boss_chromaggusAI(Creature* creature) : BossAI(creature, DATA_CHROMAGGUS) { Initialize(); @@ -193,11 +193,6 @@ public: void EnterCombat(Unit* /*who*/) override { - if (instance->GetBossState(BOSS_FLAMEGOR) != DONE) - { - EnterEvadeMode(); - return; - } _EnterCombat(); events.ScheduleEvent(EVENT_SHIMMER, 0); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_ebonroc.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_ebonroc.cpp index 8bd3ae0ebce..7a7e30f7913 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_ebonroc.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_ebonroc.cpp @@ -41,15 +41,10 @@ public: struct boss_ebonrocAI : public BossAI { - boss_ebonrocAI(Creature* creature) : BossAI(creature, BOSS_EBONROC) { } + boss_ebonrocAI(Creature* creature) : BossAI(creature, DATA_EBONROC) { } void EnterCombat(Unit* /*who*/) override { - if (instance->GetBossState(BOSS_BROODLORD) != DONE) - { - EnterEvadeMode(); - return; - } _EnterCombat(); events.ScheduleEvent(EVENT_SHADOWFLAME, urand(10000, 20000)); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_firemaw.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_firemaw.cpp index e41153796e5..3dcdbfe01df 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_firemaw.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_firemaw.cpp @@ -41,15 +41,10 @@ public: struct boss_firemawAI : public BossAI { - boss_firemawAI(Creature* creature) : BossAI(creature, BOSS_FIREMAW) { } + boss_firemawAI(Creature* creature) : BossAI(creature, DATA_FIREMAW) { } void EnterCombat(Unit* /*who*/) override { - if (instance->GetBossState(BOSS_BROODLORD) != DONE) - { - EnterEvadeMode(); - return; - } _EnterCombat(); events.ScheduleEvent(EVENT_SHADOWFLAME, urand(10000, 20000)); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_flamegor.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_flamegor.cpp index 011c695b61f..20e69211da2 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_flamegor.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_flamegor.cpp @@ -46,15 +46,10 @@ public: struct boss_flamegorAI : public BossAI { - boss_flamegorAI(Creature* creature) : BossAI(creature, BOSS_FLAMEGOR) { } + boss_flamegorAI(Creature* creature) : BossAI(creature, DATA_FLAMEGOR) { } void EnterCombat(Unit* /*who*/) override { - if (instance->GetBossState(BOSS_BROODLORD) != DONE) - { - EnterEvadeMode(); - return; - } _EnterCombat(); events.ScheduleEvent(EVENT_SHADOWFLAME, urand(10000, 20000)); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp index 94d8c2f9c2e..cbb1447c261 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp @@ -166,7 +166,7 @@ public: struct boss_victor_nefariusAI : public BossAI { - boss_victor_nefariusAI(Creature* creature) : BossAI(creature, BOSS_NEFARIAN) + boss_victor_nefariusAI(Creature* creature) : BossAI(creature, DATA_NEFARIAN) { Initialize(); } @@ -393,7 +393,7 @@ public: struct boss_nefarianAI : public BossAI { - boss_nefarianAI(Creature* creature) : BossAI(creature, BOSS_NEFARIAN) + boss_nefarianAI(Creature* creature) : BossAI(creature, DATA_NEFARIAN) { Initialize(); } @@ -457,7 +457,7 @@ public: { if (canDespawn && DespawnTimer <= diff) { - instance->SetBossState(BOSS_NEFARIAN, FAIL); + instance->SetBossState(DATA_NEFARIAN, FAIL); std::list<Creature*> constructList; me->GetCreatureListWithEntryInGrid(constructList, NPC_BONE_CONSTRUCT, 500.0f); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp index 8d660fc17d1..1d66964ce6b 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp @@ -68,7 +68,7 @@ public: struct boss_razorgoreAI : public BossAI { - boss_razorgoreAI(Creature* creature) : BossAI(creature, BOSS_RAZORGORE) + boss_razorgoreAI(Creature* creature) : BossAI(creature, DATA_RAZORGORE_THE_UNTAMED) { Initialize(); } @@ -175,7 +175,7 @@ public: { if (InstanceScript* instance = go->GetInstanceScript()) if (instance->GetData(DATA_EGG_EVENT) != DONE) - if (Creature* razor = ObjectAccessor::GetCreature(*go, instance->GetGuidData(DATA_RAZORGORE_THE_UNTAMED))) + if (Creature* razor = instance->GetCreature(DATA_RAZORGORE_THE_UNTAMED)) { razor->Attack(player, true); player->CastSpell(razor, SPELL_MINDCONTROL); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp index 827cc34d1a9..cb1306c3ea8 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp @@ -68,7 +68,7 @@ public: struct boss_vaelAI : public BossAI { - boss_vaelAI(Creature* creature) : BossAI(creature, BOSS_VAELASTRAZ) + boss_vaelAI(Creature* creature) : BossAI(creature, DATA_VAELASTRAZ_THE_CORRUPT) { Initialize(); creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp index b8843afef98..9ba255991c0 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp @@ -15,23 +15,36 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ScriptMgr.h" +#include "Player.h" #include "ScriptedCreature.h" -#include "PassiveAI.h" +#include "ScriptMgr.h" #include "blackwing_lair.h" -#include "Player.h" -/* -Blackwing Lair Encounter: -1 - boss_razorgore.cpp -2 - boss_vaelastrasz.cpp -3 - boss_broodlord_lashlayer.cpp -4 - boss_firemaw.cpp -5 - boss_ebonroc.cpp -6 - boss_flamegor.cpp -7 - boss_chromaggus.cpp -8 - boss_nefarian.cpp -*/ +DoorData const doorData[] = +{ + { GO_BOSSGATE01, DATA_RAZORGORE_THE_UNTAMED, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { GO_DRAKE_RIDER_PORTCULLIS, DATA_VAELASTRAZ_THE_CORRUPT, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { GO_ALTERAC_VALLEY_GATE, DATA_BROODLORD_LASHLAYER, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { GO_GATE, DATA_FIREMAW, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { GO_GATE, DATA_EBONROC, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { GO_GATE, DATA_FLAMEGOR, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { GO_VACCUUM_EXIT_GATE, DATA_CHROMAGGUS, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { 0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE } // END +}; + +ObjectData const creatureData[] = +{ + { NPC_RAZORGORE, DATA_RAZORGORE_THE_UNTAMED }, + { NPC_VAELASTRAZ, DATA_VAELASTRAZ_THE_CORRUPT }, + { NPC_BROODLORD, DATA_BROODLORD_LASHLAYER }, + { NPC_FIREMAW, DATA_FIREMAW }, + { NPC_EBONROC, DATA_EBONROC }, + { NPC_FLAMEGOR, DATA_FLAMEGOR }, + { NPC_CHROMAGGUS, DATA_CHROMAGGUS }, + { NPC_NEFARIAN, DATA_NEFARIAN }, + { NPC_VICTOR_NEFARIUS, DATA_LORD_VICTOR_NEFARIUS }, + { 0, 0 } // END +}; Position const SummonPosition[8] = { @@ -57,92 +70,84 @@ public: instance_blackwing_lair_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); + SetBossNumber(EncounterCount); + LoadDoorData(doorData); + LoadObjectData(creatureData, nullptr); + // Razorgore EggCount = 0; EggEvent = 0; - SetBossNumber(EncounterCount); } void OnCreatureCreate(Creature* creature) override { + InstanceScript::OnCreatureCreate(creature); + switch (creature->GetEntry()) { - case NPC_RAZORGORE: - RazorgoreTheUntamedGUID = creature->GetGUID(); - break; case NPC_BLACKWING_DRAGON: case NPC_BLACKWING_TASKMASTER: case NPC_BLACKWING_LEGIONAIRE: case NPC_BLACKWING_WARLOCK: - if (Creature* razor = instance->GetCreature(RazorgoreTheUntamedGUID)) + if (Creature* razor = GetCreature(DATA_RAZORGORE_THE_UNTAMED)) razor->AI()->JustSummoned(creature); break; - case NPC_VAELASTRAZ: - VaelastraszTheCorruptGUID = creature->GetGUID(); - break; - case NPC_BROODLORD: - BroodlordLashlayerGUID = creature->GetGUID(); - break; - case NPC_FIRENAW: - FiremawGUID = creature->GetGUID(); - break; - case NPC_EBONROC: - EbonrocGUID = creature->GetGUID(); - break; - case NPC_FLAMEGOR: - FlamegorGUID = creature->GetGUID(); - break; - case NPC_CHROMAGGUS: - ChromaggusGUID = creature->GetGUID(); - break; - case NPC_VICTOR_NEFARIUS: - LordVictorNefariusGUID = creature->GetGUID(); - break; - case NPC_NEFARIAN: - NefarianGUID = creature->GetGUID(); + default: break; } } void OnGameObjectCreate(GameObject* go) override { - switch (go->GetEntry()) + InstanceScript::OnGameObjectCreate(go); + + if (go->GetEntry() == GO_BLACK_DRAGON_EGG) { - case 177807: // Egg - if (GetBossState(BOSS_FIREMAW) == DONE) - go->SetLootState(GO_JUST_DEACTIVATED); - else - EggList.push_back(go->GetGUID()); - break; - case 175946: // Door - RazorgoreDoorGUID = go->GetGUID(); - HandleGameObject(ObjectGuid::Empty, GetBossState(BOSS_RAZORGORE) == DONE, go); - break; - case 175185: // Door - VaelastraszDoorGUID = go->GetGUID(); - HandleGameObject(ObjectGuid::Empty, GetBossState(BOSS_VAELASTRAZ) == DONE, go); - break; - case 180424: // Door - BroodlordDoorGUID = go->GetGUID(); - HandleGameObject(ObjectGuid::Empty, GetBossState(BOSS_BROODLORD) == DONE, go); - break; - case 185483: // Door - ChrommagusDoorGUID = go->GetGUID(); - HandleGameObject(ObjectGuid::Empty, GetBossState(BOSS_FIREMAW) == DONE && GetBossState(BOSS_EBONROC) == DONE && GetBossState(BOSS_FLAMEGOR) == DONE, go); - break; - case 181125: // Door - NefarianDoorGUID = go->GetGUID(); - HandleGameObject(ObjectGuid::Empty, GetBossState(BOSS_CHROMAGGUS) == DONE, go); - break; + if (GetBossState(DATA_FIREMAW) == DONE) + go->SetPhaseMask(2, true); + else + EggList.push_back(go->GetGUID()); } } void OnGameObjectRemove(GameObject* go) override { - if (go->GetEntry() == 177807) // Egg + InstanceScript::OnGameObjectRemove(go); + + if (go->GetEntry() == GO_BLACK_DRAGON_EGG) EggList.remove(go->GetGUID()); } + bool CheckRequiredBosses(uint32 bossId, Player const* player /*= nullptr*/) const override + { + if (_SkipCheckRequiredBosses(player)) + return true; + + switch (bossId) + { + case DATA_BROODLORD_LASHLAYER: + if (GetBossState(DATA_VAELASTRAZ_THE_CORRUPT) != DONE) + return false; + break; + case DATA_FIREMAW: + case DATA_EBONROC: + case DATA_FLAMEGOR: + if (GetBossState(DATA_BROODLORD_LASHLAYER) != DONE) + return false; + break; + case DATA_CHROMAGGUS: + if (GetBossState(DATA_FIREMAW) != DONE + || GetBossState(DATA_EBONROC) != DONE + || GetBossState(DATA_FLAMEGOR) != DONE) + return false; + break; + default: + break; + } + + return true; + } + bool SetBossState(uint32 type, EncounterState state) override { if (!InstanceScript::SetBossState(type, state)) @@ -150,40 +155,25 @@ public: switch (type) { - case BOSS_RAZORGORE: - HandleGameObject(RazorgoreDoorGUID, state == DONE); + case DATA_RAZORGORE_THE_UNTAMED: if (state == DONE) { for (GuidList::const_iterator itr = EggList.begin(); itr != EggList.end(); ++itr) - if (GameObject* egg = instance->GetGameObject((*itr))) + if (GameObject* egg = instance->GetGameObject(*itr)) egg->SetLootState(GO_JUST_DEACTIVATED); } SetData(DATA_EGG_EVENT, NOT_STARTED); break; - case BOSS_VAELASTRAZ: - HandleGameObject(VaelastraszDoorGUID, state == DONE); - break; - case BOSS_BROODLORD: - HandleGameObject(BroodlordDoorGUID, state == DONE); - break; - case BOSS_FIREMAW: - case BOSS_EBONROC: - case BOSS_FLAMEGOR: - HandleGameObject(ChrommagusDoorGUID, GetBossState(BOSS_FIREMAW) == DONE && GetBossState(BOSS_EBONROC) == DONE && GetBossState(BOSS_FLAMEGOR) == DONE); - break; - case BOSS_CHROMAGGUS: - HandleGameObject(NefarianDoorGUID, state == DONE); - break; - case BOSS_NEFARIAN: + case DATA_NEFARIAN: switch (state) { case NOT_STARTED: - if (Creature* nefarian = instance->GetCreature(NefarianGUID)) + if (Creature* nefarian = GetCreature(DATA_NEFARIAN)) nefarian->DespawnOrUnsummon(); break; case FAIL: - _events.ScheduleEvent(EVENT_RESPAWN_NEFARIUS, 15*IN_MILLISECONDS*MINUTE); - SetBossState(BOSS_NEFARIAN, NOT_STARTED); + _events.ScheduleEvent(EVENT_RESPAWN_NEFARIUS, 15 * IN_MILLISECONDS * MINUTE); + SetBossState(DATA_NEFARIAN, NOT_STARTED); break; default: break; @@ -193,24 +183,6 @@ public: return true; } - ObjectGuid GetGuidData(uint32 id) const override - { - switch (id) - { - case DATA_RAZORGORE_THE_UNTAMED: return RazorgoreTheUntamedGUID; - case DATA_VAELASTRAZ_THE_CORRUPT: return VaelastraszTheCorruptGUID; - case DATA_BROODLORD_LASHLAYER: return BroodlordLashlayerGUID; - case DATA_FIRENAW: return FiremawGUID; - case DATA_EBONROC: return EbonrocGUID; - case DATA_FLAMEGOR: return FlamegorGUID; - case DATA_CHROMAGGUS: return ChromaggusGUID; - case DATA_LORD_VICTOR_NEFARIUS: return LordVictorNefariusGUID; - case DATA_NEFARIAN: return NefarianGUID; - } - - return ObjectGuid::Empty; - } - void SetData(uint32 type, uint32 data) override { if (type == DATA_EGG_EVENT) @@ -218,7 +190,7 @@ public: switch (data) { case IN_PROGRESS: - _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 45*IN_MILLISECONDS); + _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 45 * IN_MILLISECONDS); EggEvent = data; EggCount = 0; break; @@ -230,13 +202,13 @@ public: case SPECIAL: if (++EggCount == 15) { - if (Creature* razor = instance->GetCreature(RazorgoreTheUntamedGUID)) + if (Creature* razor = GetCreature(DATA_RAZORGORE_THE_UNTAMED)) { SetData(DATA_EGG_EVENT, DONE); razor->RemoveAurasDueToSpell(42013); // MindControl DoRemoveAurasDueToSpellOnPlayers(42013); } - _events.ScheduleEvent(EVENT_RAZOR_PHASE_TWO, IN_MILLISECONDS); + _events.ScheduleEvent(EVENT_RAZOR_PHASE_TWO, 1 * IN_MILLISECONDS); _events.CancelEvent(EVENT_RAZOR_SPAWN); } if (EggEvent == NOT_STARTED) @@ -249,8 +221,8 @@ public: void OnUnitDeath(Unit* unit) override { //! HACK, needed because of buggy CreatureAI after charm - if (unit->GetEntry() == NPC_RAZORGORE && GetBossState(BOSS_RAZORGORE) != DONE) - SetBossState(BOSS_RAZORGORE, DONE); + if (unit->GetEntry() == NPC_RAZORGORE && GetBossState(DATA_RAZORGORE_THE_UNTAMED) != DONE) + SetBossState(DATA_RAZORGORE_THE_UNTAMED, DONE); } void Update(uint32 diff) override @@ -268,15 +240,15 @@ public: for (uint8 i = urand(2, 5); i > 0 ; --i) if (Creature* summon = instance->SummonCreature(Entry[urand(0, 4)], SummonPosition[urand(0, 7)])) summon->SetInCombatWithZone(); - _events.ScheduleEvent(EVENT_RAZOR_SPAWN, urand(12, 17)*IN_MILLISECONDS); + _events.ScheduleEvent(EVENT_RAZOR_SPAWN, urand(12, 17) * IN_MILLISECONDS); break; case EVENT_RAZOR_PHASE_TWO: _events.CancelEvent(EVENT_RAZOR_SPAWN); - if (Creature* razor = instance->GetCreature(RazorgoreTheUntamedGUID)) + if (Creature* razor = GetCreature(DATA_RAZORGORE_THE_UNTAMED)) razor->AI()->DoAction(ACTION_PHASE_TWO); break; case EVENT_RESPAWN_NEFARIUS: - if (Creature* nefarius = instance->GetCreature(LordVictorNefariusGUID)) + if (Creature* nefarius = GetCreature(DATA_LORD_VICTOR_NEFARIUS)) { nefarius->SetPhaseMask(1, true); nefarius->setActive(true); @@ -291,34 +263,11 @@ public: protected: // Misc EventMap _events; + // Razorgore uint8 EggCount; uint32 EggEvent; - ObjectGuid RazorgoreTheUntamedGUID; - ObjectGuid RazorgoreDoorGUID; GuidList EggList; - - // Vaelastrasz the Corrupt - ObjectGuid VaelastraszTheCorruptGUID; - ObjectGuid VaelastraszDoorGUID; - - // Broodlord Lashlayer - ObjectGuid BroodlordLashlayerGUID; - ObjectGuid BroodlordDoorGUID; - - // 3 Dragons - ObjectGuid FiremawGUID; - ObjectGuid EbonrocGUID; - ObjectGuid FlamegorGUID; - ObjectGuid ChrommagusDoorGUID; - - // Chormaggus - ObjectGuid ChromaggusGUID; - ObjectGuid NefarianDoorGUID; - - // Nefarian - ObjectGuid LordVictorNefariusGUID; - ObjectGuid NefarianGUID; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/EasternKingdoms/CMakeLists.txt b/src/server/scripts/EasternKingdoms/CMakeLists.txt index 754f0173c97..f3ba8f8d470 100644 --- a/src/server/scripts/EasternKingdoms/CMakeLists.txt +++ b/src/server/scripts/EasternKingdoms/CMakeLists.txt @@ -29,12 +29,9 @@ set(scripts_STAT_SRCS EasternKingdoms/BlackrockMountain/BlackrockCaverns/instance_blackrock_caverns.cpp EasternKingdoms/BlackrockMountain/BlackrockCaverns/blackrock_caverns.h EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_high_interrogator_gerstahn.cpp - EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_gorosh_the_dervish.cpp EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp - EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_anubshiah.cpp EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_tomb_of_seven.cpp EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_general_angerforge.cpp - EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_grizzle.cpp EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_ambassador_flamelash.cpp EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_moira_bronzebeard.cpp diff --git a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp index 87dadd44dd7..c1470d3bc47 100644 --- a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp +++ b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp @@ -361,7 +361,7 @@ public: } } - void UpdateEscortAI(const uint32 uiDiff) override + void UpdateEscortAI(uint32 uiDiff) override { if (uiPhase) { diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp index 238ca194a65..44d48e3f0bb 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp @@ -731,13 +731,19 @@ class npc_dark_rider_of_acherus : public CreatureScript ## npc_salanar_the_horseman ######*/ -enum Spells_Salanar +enum SalanarTheHorseman { - SPELL_REALM_OF_SHADOWS = 52693, + GOSSIP_SALANAR_MENU = 9739, + GOSSIP_SALANAR_OPTION = 0, + SALANAR_SAY = 0, + QUEST_INTO_REALM_OF_SHADOWS = 12687, + NPC_DARK_RIDER_OF_ACHERUS = 28654, + NPC_SALANAR_IN_REALM_OF_SHADOWS = 28788, SPELL_EFFECT_STOLEN_HORSE = 52263, SPELL_DELIVER_STOLEN_HORSE = 52264, SPELL_CALL_DARK_RIDER = 52266, - SPELL_EFFECT_OVERTAKE = 52349 + SPELL_EFFECT_OVERTAKE = 52349, + SPELL_REALM_OF_SHADOWS = 52693 }; class npc_salanar_the_horseman : public CreatureScript @@ -745,15 +751,19 @@ class npc_salanar_the_horseman : public CreatureScript public: npc_salanar_the_horseman() : CreatureScript("npc_salanar_the_horseman") { } - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_salanar_the_horsemanAI(creature); - } - struct npc_salanar_the_horsemanAI : public ScriptedAI { npc_salanar_the_horsemanAI(Creature* creature) : ScriptedAI(creature) { } + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override + { + if (menuId == GOSSIP_SALANAR_MENU && gossipListId == GOSSIP_SALANAR_OPTION) + { + player->CastSpell(player, SPELL_REALM_OF_SHADOWS, true); + player->PlayerTalkClass->SendCloseGossip(); + } + } + void SpellHit(Unit* caster, const SpellInfo* spell) override { if (spell->Id == SPELL_DELIVER_STOLEN_HORSE) @@ -768,7 +778,7 @@ public: caster->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); caster->setFaction(35); DoCast(caster, SPELL_CALL_DARK_RIDER, true); - if (Creature* Dark_Rider = me->FindNearestCreature(28654, 15)) + if (Creature* Dark_Rider = me->FindNearestCreature(NPC_DARK_RIDER_OF_ACHERUS, 15)) ENSURE_AI(npc_dark_rider_of_acherus::npc_dark_rider_of_acherusAI, Dark_Rider->AI())->InitDespawnHorse(caster); } } @@ -786,10 +796,11 @@ public: { if (Player* player = charmer->ToPlayer()) { - // for quest Into the Realm of Shadows(12687) - if (me->GetEntry() == 28788 && player->GetQuestStatus(12687) == QUEST_STATUS_INCOMPLETE) + // for quest Into the Realm of Shadows(QUEST_INTO_REALM_OF_SHADOWS) + if (me->GetEntry() == NPC_SALANAR_IN_REALM_OF_SHADOWS && player->GetQuestStatus(QUEST_INTO_REALM_OF_SHADOWS) == QUEST_STATUS_INCOMPLETE) { - player->GroupEventHappens(12687, me); + player->GroupEventHappens(QUEST_INTO_REALM_OF_SHADOWS, me); + Talk(SALANAR_SAY); charmer->RemoveAurasDueToSpell(SPELL_EFFECT_OVERTAKE); if (Creature* creature = who->ToCreature()) { @@ -798,14 +809,17 @@ public: } } - if (player->HasAura(SPELL_REALM_OF_SHADOWS)) - player->RemoveAurasDueToSpell(SPELL_REALM_OF_SHADOWS); + player->RemoveAurasDueToSpell(SPELL_REALM_OF_SHADOWS); } } } } }; + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_salanar_the_horsemanAI(creature); + } }; /*###### diff --git a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp index 15280bc7848..29002460b2a 100644 --- a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp +++ b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp @@ -41,14 +41,17 @@ enum Says enum Spells { - SPELL_GROUND_TREMOR = 6524, - SPELL_ARCHAEDAS_AWAKEN = 10347, - SPELL_BOSS_OBJECT_VISUAL = 11206, - SPELL_BOSS_AGGRO = 10340, - SPELL_SUB_BOSS_AGGRO = 11568, - SPELL_AWAKEN_VAULT_WALKER = 10258, - SPELL_AWAKEN_EARTHEN_GUARDIAN = 10252, - SPELL_SELF_DESTRUCT = 9874 + SPELL_GROUND_TREMOR = 6524, + SPELL_ARCHAEDAS_AWAKEN = 10347, + SPELL_BOSS_OBJECT_VISUAL = 11206, + SPELL_BOSS_AGGRO = 10340, + SPELL_SUB_BOSS_AGGRO = 11568, + SPELL_AWAKEN_VAULT_WALKER = 10258, + SPELL_AWAKEN_EARTHEN_GUARDIAN = 10252, + SPELL_SELF_DESTRUCT = 9874, + SPELL_FREEZE_ANIM = 16245, + SPELL_MINION_FREEZE_ANIM = 10255 + }; class boss_archaedas : public CreatureScript @@ -96,6 +99,7 @@ class boss_archaedas : public CreatureScript me->setFaction(35); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->AddAura(SPELL_FREEZE_ANIM, me); } void ActivateMinion(ObjectGuid uiGuid, bool flag) @@ -109,6 +113,7 @@ class boss_archaedas : public CreatureScript minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); minion->setFaction(14); + minion->RemoveAura(SPELL_MINION_FREEZE_ANIM); } } @@ -258,6 +263,7 @@ class npc_archaedas_minions : public CreatureScript me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); me->RemoveAllAuras(); + me->AddAura(SPELL_MINION_FREEZE_ANIM, me); } void EnterCombat(Unit* /*who*/) override @@ -297,7 +303,7 @@ class npc_archaedas_minions : public CreatureScript { bWakingUp = false; bAmIAwake = true; - // AttackStart(ObjectAccessor::GetUnit(*me, instance->GetGuidData(0))); // whoWokeArchaedasGUID + AttackStart(ObjectAccessor::GetUnit(*me, instance->GetGuidData(0))); // whoWokeArchaedasGUID return; // dont want to continue until we finish the AttackStart method } @@ -346,6 +352,7 @@ class npc_stonekeepers : public CreatureScript me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); me->RemoveAllAuras(); + me->AddAura(SPELL_MINION_FREEZE_ANIM, me); } void EnterCombat(Unit* /*who*/) override diff --git a/src/server/scripts/EasternKingdoms/Uldaman/boss_ironaya.cpp b/src/server/scripts/EasternKingdoms/Uldaman/boss_ironaya.cpp index de899c04e1a..7f0faa138ec 100644 --- a/src/server/scripts/EasternKingdoms/Uldaman/boss_ironaya.cpp +++ b/src/server/scripts/EasternKingdoms/Uldaman/boss_ironaya.cpp @@ -28,10 +28,9 @@ EndScriptData */ enum Ironaya { - SAY_AGGRO = 0, SPELL_ARCINGSMASH = 8374, SPELL_KNOCKAWAY = 10101, - SPELL_WSTOMP = 11876, + SPELL_WSTOMP = 11876 }; class boss_ironaya : public CreatureScript @@ -66,10 +65,7 @@ class boss_ironaya : public CreatureScript Initialize(); } - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - } + void EnterCombat(Unit* /*who*/) override { } void UpdateAI(uint32 uiDiff) override { diff --git a/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp b/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp index 9d33d41af62..b59c040d90b 100644 --- a/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp +++ b/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp @@ -18,7 +18,7 @@ /* ScriptData SDName: instance_uldaman -SD%Complete: 99 +SD%Complete: 80% SDComment: Need some cosmetics updates when archeadas door are closing (Guardians Waypoints). SDCategory: Uldaman EndScriptData */ @@ -31,6 +31,8 @@ enum Spells { SPELL_ARCHAEDAS_AWAKEN = 10347, SPELL_AWAKEN_VAULT_WALKER = 10258, + SPELL_FREEZE_ANIM = 16245, + SPELL_MINION_FREEZE_ANIM = 10255 }; enum Events @@ -38,6 +40,13 @@ enum Events EVENT_SUB_BOSS_AGGRO = 2228 }; +enum IronayaTalk +{ + SAY_AGGRO = 0 +}; + +const Position IronayaPoint = { -231.228f, 246.6135f, -49.01617f, 0.0f }; + class instance_uldaman : public InstanceMapScript { public: @@ -136,9 +145,9 @@ class instance_uldaman : public InstanceMapScript { creature->setFaction(35); creature->RemoveAllAuras(); - //creature->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_ANIMATION_FROZEN); creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + creature->AddAura(SPELL_MINION_FREEZE_ANIM, creature); } void SetDoor(ObjectGuid guid, bool open) @@ -171,6 +180,8 @@ class instance_uldaman : public InstanceMapScript target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); target->setFaction(14); target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + target->RemoveAura(SPELL_MINION_FREEZE_ANIM); + return; // only want the first one we find } // if we get this far than all four are dead so open the door @@ -190,11 +201,13 @@ class instance_uldaman : public InstanceMapScript Creature* target = instance->GetCreature(*i); if (!target || !target->IsAlive() || target->getFaction() == 14) continue; - archaedas->CastSpell(target, SPELL_AWAKEN_VAULT_WALKER, true); - target->CastSpell(target, SPELL_ARCHAEDAS_AWAKEN, true); target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); target->setFaction(14); + target->RemoveAura(SPELL_MINION_FREEZE_ANIM); + archaedas->CastSpell(target, SPELL_AWAKEN_VAULT_WALKER, true); + target->CastSpell(target, SPELL_ARCHAEDAS_AWAKEN, true); + return; // only want the first one we find } } @@ -241,6 +254,7 @@ class instance_uldaman : public InstanceMapScript if (ObjectAccessor::GetUnit(*archaedas, target)) { + archaedas->RemoveAura(SPELL_FREEZE_ANIM); archaedas->CastSpell(archaedas, SPELL_ARCHAEDAS_AWAKEN, false); whoWokeuiArchaedasGUID = target; } @@ -255,6 +269,12 @@ class instance_uldaman : public InstanceMapScript ironaya->setFaction(415); ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + ironaya->GetMotionMaster()->Clear(); + ironaya->GetMotionMaster()->MovePoint(0, IronayaPoint); + ironaya->SetHomePosition(IronayaPoint); + + ironaya->AI()->Talk(SAY_AGGRO); } void RespawnMinions() diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index 1c9d5cf2c62..978fc6d96fc 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -607,7 +607,7 @@ public: summoned->AI()->AttackStart(me); } - void UpdateEscortAI(const uint32 Diff) override + void UpdateEscortAI(uint32 Diff) override { if (!UpdateVictim()) { diff --git a/src/server/scripts/Kalimdor/zone_winterspring.cpp b/src/server/scripts/Kalimdor/zone_winterspring.cpp index b287e5e9a91..067f36afad6 100644 --- a/src/server/scripts/Kalimdor/zone_winterspring.cpp +++ b/src/server/scripts/Kalimdor/zone_winterspring.cpp @@ -568,7 +568,7 @@ public: } - void UpdateEscortAI(const uint32 diff) override + void UpdateEscortAI(uint32 diff) override { DialogueUpdate(diff); diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp index eb6230fabfc..eb004505bab 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp @@ -263,7 +263,7 @@ class npc_onyx_flamecaller : public CreatureScript } } - void UpdateEscortAI(uint32 const diff) override + void UpdateEscortAI(uint32 diff) override { if (!UpdateVictim()) return; diff --git a/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp b/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp index 5111247b84c..4438c4ab199 100644 --- a/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp +++ b/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp @@ -18,7 +18,6 @@ #include "InstanceScript.h" #include "Player.h" #include "ScriptMgr.h" -#include "WorldSession.h" #include "gundrak.h" #include "EventMap.h" @@ -191,7 +190,7 @@ class instance_gundrak : public InstanceMapScript bool CheckRequiredBosses(uint32 bossId, Player const* player = nullptr) const override { - if (player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES)) + if (_SkipCheckRequiredBosses(player)) return true; switch (bossId) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp index c832efabbe7..6a7c6a1fd5c 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp @@ -203,7 +203,7 @@ class boss_blood_queen_lana_thel : public CreatureScript { instance->SetData(DATA_BLOOD_QUICKENING_STATE, DONE); if (Player* player = killer->ToPlayer()) - player->RewardPlayerAndGroupAtEvent(NPC_INFILTRATOR_MINCHAR_BQ, player); + player->RewardPlayerAndGroupAtEvent(Is25ManRaid() ? NPC_INFILTRATOR_MINCHAR_BQ_25 : NPC_INFILTRATOR_MINCHAR_BQ, player); if (Creature* minchar = me->FindNearestCreature(NPC_INFILTRATOR_MINCHAR_BQ, 200.0f)) { minchar->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp index 943be27943d..365e0a1588d 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp @@ -203,6 +203,9 @@ enum Misc { DATA_MADE_A_MESS = 45374613, // 4537, 4613 are achievement IDs FACTION_SCOURGE = 974, + + GOSSIP_MENU_MURADIN_BRONZEBEARD = 10934, + GOSSIP_MENU_HIGH_OVERLORD_SAURFANG = 10952 }; enum MovePoints @@ -634,6 +637,15 @@ class npc_high_overlord_saurfang_icc : public CreatureScript _events.Reset(); } + void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override + { + if (menuId == GOSSIP_MENU_HIGH_OVERLORD_SAURFANG) + { + player->PlayerTalkClass->SendCloseGossip(); + DoAction(ACTION_START_EVENT); + } + } + void DoAction(int32 action) override { switch (action) @@ -798,28 +810,6 @@ class npc_high_overlord_saurfang_icc : public CreatureScript std::list<Creature*> _guardList; }; - bool OnGossipHello(Player* player, Creature* creature) override - { - InstanceScript* instance = creature->GetInstanceScript(); - if (instance && instance->GetBossState(DATA_DEATHBRINGER_SAURFANG) != DONE) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "We are ready to go, High Overlord. The Lich King must fall!", 631, -ACTION_START_EVENT); - player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); - } - - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - player->CLOSE_GOSSIP_MENU(); - if (action == -ACTION_START_EVENT) - creature->AI()->DoAction(ACTION_START_EVENT); - - return true; - } - CreatureAI* GetAI(Creature* creature) const override { return GetIcecrownCitadelAI<npc_high_overlord_saurfangAI>(creature); @@ -843,6 +833,15 @@ class npc_muradin_bronzebeard_icc : public CreatureScript _events.Reset(); } + void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override + { + if (menuId == GOSSIP_MENU_MURADIN_BRONZEBEARD) + { + player->PlayerTalkClass->SendCloseGossip(); + DoAction(ACTION_START_EVENT); + } + } + void DoAction(int32 action) override { switch (action) @@ -946,28 +945,6 @@ class npc_muradin_bronzebeard_icc : public CreatureScript std::list<Creature*> _guardList; }; - bool OnGossipHello(Player* player, Creature* creature) override - { - InstanceScript* instance = creature->GetInstanceScript(); - if (instance && instance->GetBossState(DATA_DEATHBRINGER_SAURFANG) != DONE) - { - player->ADD_GOSSIP_ITEM(0, "Let it begin...", 631, -ACTION_START_EVENT + 1); - player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); - } - - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - player->CLOSE_GOSSIP_MENU(); - if (action == -ACTION_START_EVENT + 1) - creature->AI()->DoAction(ACTION_START_EVENT); - - return true; - } - CreatureAI* GetAI(Creature* creature) const override { return GetIcecrownCitadelAI<npc_muradin_bronzebeard_iccAI>(creature); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp index c04d53079c5..4a8f56d0d55 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp @@ -1336,7 +1336,7 @@ class spell_dreamwalker_summon_dream_portal : public SpellScriptLoader if (!GetHitUnit()) return; - uint32 spellId = RAND<uint32>(71301, 72220, 72223, 72225); + uint32 spellId = RAND(71301, 72220, 72223, 72225); GetHitUnit()->CastSpell(GetHitUnit(), spellId, true); } @@ -1367,7 +1367,7 @@ class spell_dreamwalker_summon_nightmare_portal : public SpellScriptLoader if (!GetHitUnit()) return; - uint32 spellId = RAND<uint32>(71977, 72481, 72482, 72483); + uint32 spellId = RAND(71977, 72481, 72482, 72483); GetHitUnit()->CastSpell(GetHitUnit(), spellId, true); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index 30b797bf98d..8676b6c0506 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -1140,7 +1140,7 @@ class npc_crok_scourgebane : public CreatureScript } } - void UpdateEscortAI(uint32 const diff) override + void UpdateEscortAI(uint32 diff) override { if (_wipeCheckTimer <= diff) _wipeCheckTimer = 0; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h index 091190b6b4e..e739f5a5036 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h @@ -155,6 +155,7 @@ enum CreaturesIds NPC_ALCHEMIST_ADRIANNA = 38501, NPC_ALRIN_THE_AGILE = 38551, NPC_INFILTRATOR_MINCHAR_BQ = 38558, + NPC_INFILTRATOR_MINCHAR_BQ_25 = 39123, NPC_MINCHAR_BEAM_STALKER = 38557, NPC_VALITHRIA_DREAMWALKER_QUEST = 38589, diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index d38d630f775..555ed40e805 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -17,7 +17,6 @@ #include "AccountMgr.h" #include "InstanceScript.h" -#include "Map.h" #include "ObjectMgr.h" #include "Player.h" #include "PoolMgr.h" @@ -26,7 +25,6 @@ #include "Transport.h" #include "TransportMgr.h" #include "WorldPacket.h" -#include "WorldSession.h" #include "icecrown_citadel.h" enum EventIds @@ -1118,7 +1116,7 @@ class instance_icecrown_citadel : public InstanceMapScript bool CheckRequiredBosses(uint32 bossId, Player const* player = nullptr) const override { - if (player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES)) + if (_SkipCheckRequiredBosses(player)) return true; switch (bossId) diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp index 28d181a990e..998d519c7fe 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp @@ -17,39 +17,61 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "Player.h" #include "naxxramas.h" -enum Says +enum AnubSays { SAY_AGGRO = 0, SAY_GREET = 1, - SAY_SLAY = 2 + SAY_SLAY = 2, + + EMOTE_LOCUST = 3 }; -Position const GuardSummonPos = {3333.72f, -3476.30f, 287.1f, 6.2801f}; +enum GuardSays +{ + EMOTE_FRENZY = 0, + EMOTE_SPAWN = 1, + EMOTE_SCARAB = 2 +}; enum Events { - EVENT_IMPALE = 1, - EVENT_LOCUST, - EVENT_SPAWN_GUARDIAN_NORMAL, - EVENT_BERSERK + EVENT_IMPALE = 1, // Cast Impale on a random target + EVENT_LOCUST, // Begin channeling Locust Swarm + EVENT_LOCUST_ENDS, // Locust swarm dissipates + EVENT_SPAWN_GUARD, // 10-man only - crypt guard has delayed spawn; also used for the locust swarm crypt guard in both modes + EVENT_SCARABS, // spawn corpse scarabs + EVENT_BERSERK // Berserk }; enum Spells { - SPELL_IMPALE = 28783, - SPELL_LOCUST_SWARM = 28785, + SPELL_IMPALE = 28783, // 25-man: 56090 + SPELL_LOCUST_SWARM = 28785, // 25-man: 54021 SPELL_SUMMON_CORPSE_SCARABS_PLR = 29105, // This spawns 5 corpse scarabs on top of player SPELL_SUMMON_CORPSE_SCARABS_MOB = 28864, // This spawns 10 corpse scarabs on top of dead guards SPELL_BERSERK = 27680 }; +enum SpawnGroups +{ + GROUP_INITIAL_25M = 1, + GROUP_SINGLE_SPAWN = 2 +}; + enum Misc { ACHIEV_TIMED_START_EVENT = 9891 }; +enum Phases +{ + PHASE_NORMAL = 1, + PHASE_SWARM +}; + class boss_anubrekhan : public CreatureScript { public: @@ -62,46 +84,64 @@ public: struct boss_anubrekhanAI : public BossAI { - boss_anubrekhanAI(Creature* creature) : BossAI(creature, BOSS_ANUBREKHAN) + boss_anubrekhanAI(Creature* creature) : BossAI(creature, BOSS_ANUBREKHAN) { } + + void SummonGuards() { - Initialize(); + if (Is25ManRaid()) + me->SummonCreatureGroup(GROUP_INITIAL_25M); } - void Initialize() + void InitializeAI() override { - hasTaunted = false; + if (!me->isDead()) + { + Reset(); + SummonGuards(); + } } - bool hasTaunted; - void Reset() override { _Reset(); + guardCorpses.clear(); + } - Initialize(); + void JustReachedHome() override + { + _JustReachedHome(); + SummonGuards(); + } - if (GetDifficulty() == DIFFICULTY_25_N) - { - Position pos; + void JustSummoned(Creature* summon) override + { + BossAI::JustSummoned(summon); + + if (me->IsInCombat()) + if (summon->GetEntry() == NPC_CRYPT_GUARD) + summon->AI()->Talk(EMOTE_SPAWN, me); + } - // respawn guard using home position, - // otherwise, after a wipe, they respawn where boss was at wipe moment. - pos = me->GetHomePosition(); - pos.m_positionY -= 10.0f; - me->SummonCreature(NPC_CRYPT_GUARD, pos, TEMPSUMMON_CORPSE_DESPAWN); + void SummonedCreatureDies(Creature* summon, Unit* killer) override + { + BossAI::SummonedCreatureDies(summon, killer); - pos = me->GetHomePosition(); - pos.m_positionY += 10.0f; - me->SummonCreature(NPC_CRYPT_GUARD, pos, TEMPSUMMON_CORPSE_DESPAWN); - } + if (summon->GetEntry() == NPC_CRYPT_GUARD) + guardCorpses.insert(summon->GetGUID()); + } + + void SummonedCreatureDespawn(Creature* summon) override + { + BossAI::SummonedCreatureDespawn(summon); + + if (summon->GetEntry() == NPC_CRYPT_GUARD) + guardCorpses.erase(summon->GetGUID()); } void KilledUnit(Unit* victim) override { - /// Force the player to spawn corpse scarabs via spell, @todo Check percent chance for scarabs, 20% at the moment - if (!(rand32() % 5)) - if (victim->GetTypeId() == TYPEID_PLAYER) - victim->CastSpell(victim, SPELL_SUMMON_CORPSE_SCARABS_PLR, true, NULL, NULL, me->GetGUID()); + if (victim->GetTypeId() == TYPEID_PLAYER) + victim->CastSpell(victim, SPELL_SUMMON_CORPSE_SCARABS_PLR, true, nullptr, nullptr, me->GetGUID()); Talk(SAY_SLAY); } @@ -113,37 +153,22 @@ public: // start achievement timer (kill Maexna within 20 min) instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); } + void EnterCombat(Unit* /*who*/) override { _EnterCombat(); Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_IMPALE, urand(10000, 20000)); - events.ScheduleEvent(EVENT_LOCUST, 90000); - events.ScheduleEvent(EVENT_BERSERK, 600000); - - if (GetDifficulty() == DIFFICULTY_10_N) - events.ScheduleEvent(EVENT_SPAWN_GUARDIAN_NORMAL, urand(15000, 20000)); - } - void MoveInLineOfSight(Unit* who) override - { - if (!hasTaunted && me->IsWithinDistInMap(who, 60.0f) && who->GetTypeId() == TYPEID_PLAYER) - { - Talk(SAY_GREET); - hasTaunted = true; - } - ScriptedAI::MoveInLineOfSight(who); - } - - void SummonedCreatureDespawn(Creature* summon) override - { - BossAI::SummonedCreatureDespawn(summon); - - // check if it is an actual killed guard - if (!me->IsAlive() || summon->IsAlive() || summon->GetEntry() != NPC_CRYPT_GUARD) - return; + summons.DoZoneInCombat(); + + events.SetPhase(PHASE_NORMAL); + events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_SCARABS, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS), 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_LOCUST, urand(80,120) * IN_MILLISECONDS, 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS); - summon->CastSpell(summon, SPELL_SUMMON_CORPSE_SCARABS_MOB, true, NULL, NULL, me->GetGUID()); + if (!Is25ManRaid()) + events.ScheduleEvent(EVENT_SPAWN_GUARD, urand(15, 20) * IN_MILLISECONDS); } void UpdateAI(uint32 diff) override @@ -158,22 +183,43 @@ public: switch (eventId) { case EVENT_IMPALE: - //Cast Impale on a random target - //Do NOT cast it when we are afflicted by locust swarm - if (!me->HasAura(SPELL_LOCUST_SWARM)) - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_IMPALE); - events.ScheduleEvent(EVENT_IMPALE, urand(10000, 20000)); + if (events.GetTimeUntilEvent(EVENT_LOCUST) < 5 * IN_MILLISECONDS) break; // don't chain impale tank -> locust swarm + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_IMPALE); + else + EnterEvadeMode(); + + events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL); + break; + case EVENT_SCARABS: + events.ScheduleEvent(EVENT_SCARABS, urand(40 * IN_MILLISECONDS, 60 * IN_MILLISECONDS), 0, PHASE_NORMAL); + + if (!guardCorpses.empty()) + { + if (Creature* creatureTarget = ObjectAccessor::GetCreature(*me, Trinity::Containers::SelectRandomContainerElement(guardCorpses))) + { + creatureTarget->CastSpell(creatureTarget, SPELL_SUMMON_CORPSE_SCARABS_MOB, true, nullptr, nullptr, me->GetGUID()); + creatureTarget->AI()->Talk(EMOTE_SCARAB); + creatureTarget->DespawnOrUnsummon(); + } + } break; case EVENT_LOCUST: - /// @todo Add Text + Talk(EMOTE_LOCUST); DoCast(me, SPELL_LOCUST_SWARM); - DoSummon(NPC_CRYPT_GUARD, GuardSummonPos, 0, TEMPSUMMON_CORPSE_DESPAWN); + events.ScheduleEvent(EVENT_SPAWN_GUARD, 3 * IN_MILLISECONDS); + + events.ScheduleEvent(EVENT_LOCUST_ENDS, RAID_MODE(19, 23) * IN_MILLISECONDS); events.ScheduleEvent(EVENT_LOCUST, 90000); + events.SetPhase(PHASE_SWARM); + break; + case EVENT_LOCUST_ENDS: + events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_SCARABS, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS), 0, PHASE_NORMAL); + events.SetPhase(PHASE_NORMAL); break; - case EVENT_SPAWN_GUARDIAN_NORMAL: - /// @todo Add Text - DoSummon(NPC_CRYPT_GUARD, GuardSummonPos, 0, TEMPSUMMON_CORPSE_DESPAWN); + case EVENT_SPAWN_GUARD: + me->SummonCreatureGroup(GROUP_SINGLE_SPAWN); break; case EVENT_BERSERK: DoCast(me, SPELL_BERSERK, true); @@ -182,13 +228,37 @@ public: } } - DoMeleeAttackIfReady(); + if (events.IsInPhase(PHASE_NORMAL)) + DoMeleeAttackIfReady(); } + private: + GuidSet guardCorpses; }; }; +class at_anubrekhan_entrance : public AreaTriggerScript +{ + public: + at_anubrekhan_entrance() : AreaTriggerScript("at_anubrekhan_entrance") { } + + bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/, bool /*entered*/) override + { + InstanceScript* instance = player->GetInstanceScript(); + if (!instance || instance->GetData(DATA_HAD_ANUBREKHAN_GREET) || instance->GetBossState(BOSS_ANUBREKHAN) != NOT_STARTED) + return true; + + if (Creature* anub = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_ANUBREKHAN))) + anub->AI()->Talk(SAY_GREET); + instance->SetData(DATA_HAD_ANUBREKHAN_GREET, 1u); + + return true; + } +}; + void AddSC_boss_anubrekhan() { new boss_anubrekhan(); + + new at_anubrekhan_entrance(); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp index a90b72b6842..78720964518 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp @@ -18,14 +18,20 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "naxxramas.h" +#include "Player.h" +#include "SpellAuras.h" #include "SpellInfo.h" enum Yells { - SAY_GREET = 0, - SAY_AGGRO = 1, - SAY_SLAY = 2, - SAY_DEATH = 3 + SAY_GREET = 0, + SAY_AGGRO = 1, + SAY_SLAY = 2, + SAY_DEATH = 3, + + EMOTE_WIDOW_EMBRACE = 4, + EMOTE_FRENZY = 5 + }; enum Spells @@ -33,7 +39,9 @@ enum Spells SPELL_POISON_BOLT_VOLLEY = 28796, SPELL_RAIN_OF_FIRE = 28794, SPELL_FRENZY = 28798, - SPELL_WIDOWS_EMBRACE = 28732 + SPELL_WIDOWS_EMBRACE = 28732, + + SPELL_ADD_FIREBALL = 54095 // 25-man: 54096 }; #define SPELL_WIDOWS_EMBRACE_HELPER RAID_MODE<uint32>(28732, 54097) @@ -45,6 +53,12 @@ enum Events EVENT_FRENZY = 3 }; +enum SummonGroups +{ + SUMMON_GROUP_WORSHIPPERS = 1, + SUMMON_GROUP_FOLLOWERS = 2 +}; + enum Misc { DATA_FRENZY_DISPELS = 1 @@ -57,39 +71,46 @@ class boss_faerlina : public CreatureScript struct boss_faerlinaAI : public BossAI { - boss_faerlinaAI(Creature* creature) : BossAI(creature, BOSS_FAERLINA), - _frenzyDispels(0), _introDone(false), _delayFrenzy(false) + boss_faerlinaAI(Creature* creature) : BossAI(creature, BOSS_FAERLINA), _frenzyDispels(0) { } + + void SummonAdds() { + me->SummonCreatureGroup(SUMMON_GROUP_WORSHIPPERS); + if (Is25ManRaid()) + me->SummonCreatureGroup(SUMMON_GROUP_FOLLOWERS); } + void InitializeAI() override + { + if (!me->isDead()) + { + Reset(); + SummonAdds(); + } + } + + void JustReachedHome() override + { + _JustReachedHome(); + SummonAdds(); + } void EnterCombat(Unit* /*who*/) override { _EnterCombat(); Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_POISON, urand(10000, 15000)); - events.ScheduleEvent(EVENT_FIRE, urand(6000, 18000)); - events.ScheduleEvent(EVENT_FRENZY, urand(60000, 80000)); + summons.DoZoneInCombat(); + events.ScheduleEvent(EVENT_POISON, urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS)); + events.ScheduleEvent(EVENT_FIRE, urand(6 * IN_MILLISECONDS, 18 * IN_MILLISECONDS)); + events.ScheduleEvent(EVENT_FRENZY, urand(60 * IN_MILLISECONDS, 80 * IN_MILLISECONDS)); } void Reset() override { _Reset(); - _delayFrenzy = false; _frenzyDispels = 0; } - void MoveInLineOfSight(Unit* who) override - { - if (!_introDone && who->GetTypeId() == TYPEID_PLAYER) - { - Talk(SAY_GREET); - _introDone = true; - } - - BossAI::MoveInLineOfSight(who); - } - void KilledUnit(Unit* /*victim*/) override { if (!urand(0, 2)) @@ -106,9 +127,8 @@ class boss_faerlina : public CreatureScript { if (spell->Id == SPELL_WIDOWS_EMBRACE_HELPER) { - /// @todo Add Text ++_frenzyDispels; - _delayFrenzy = true; + Talk(EMOTE_WIDOW_EMBRACE, caster); me->Kill(caster); } } @@ -126,12 +146,6 @@ class boss_faerlina : public CreatureScript if (!UpdateVictim()) return; - if (_delayFrenzy && !me->HasAura(SPELL_WIDOWS_EMBRACE_HELPER)) - { - _delayFrenzy = false; - DoCast(me, SPELL_FRENZY, true); - } - events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) @@ -152,13 +166,14 @@ class boss_faerlina : public CreatureScript events.ScheduleEvent(EVENT_FIRE, urand(6000, 18000)); break; case EVENT_FRENZY: - /// @todo Add Text - if (!me->HasAura(SPELL_WIDOWS_EMBRACE_HELPER)) - DoCast(me, SPELL_FRENZY); + if (Aura* widowsEmbrace = me->GetAura(SPELL_WIDOWS_EMBRACE_HELPER)) + events.ScheduleEvent(EVENT_FRENZY, widowsEmbrace->GetDuration()+1 * IN_MILLISECONDS); else - _delayFrenzy = true; - - events.ScheduleEvent(EVENT_FRENZY, urand(60000, 80000)); + { + DoCast(SPELL_FRENZY); + Talk(EMOTE_FRENZY); + events.ScheduleEvent(EVENT_FRENZY, urand(60 * IN_MILLISECONDS, 80 * IN_MILLISECONDS)); + } break; } } @@ -168,8 +183,6 @@ class boss_faerlina : public CreatureScript private: uint32 _frenzyDispels; - bool _introDone; - bool _delayFrenzy; }; CreatureAI* GetAI(Creature* creature) const override @@ -192,19 +205,36 @@ class npc_faerlina_add : public CreatureScript void Reset() override { - if (GetDifficulty() == DIFFICULTY_10_N) { + if (!Is25ManRaid()) { me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_BIND, true); me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_CHARM, true); } } + void EnterCombat(Unit* /*who*/) override + { + if (Creature* faerlina = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FAERLINA))) + faerlina->AI()->DoZoneInCombat(nullptr, 250.0f); + } + void JustDied(Unit* /*killer*/) override { - if (_instance && GetDifficulty() == DIFFICULTY_10_N) + if (!Is25ManRaid()) if (Creature* faerlina = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FAERLINA))) DoCast(faerlina, SPELL_WIDOWS_EMBRACE); } + void UpdateAI(uint32 /*diff*/) override + { + if (!UpdateVictim()) + return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + DoCastVictim(SPELL_ADD_FIREBALL); + DoMeleeAttackIfReady(); // this will only happen if the fireball cast fails for some reason + } + private: InstanceScript* const _instance; }; @@ -226,9 +256,29 @@ class achievement_momma_said_knock_you_out : public AchievementCriteriaScript } }; +class at_faerlina_entrance : public AreaTriggerScript +{ + public: + at_faerlina_entrance() : AreaTriggerScript("at_faerlina_entrance") { } + + bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/, bool /*entered*/) override + { + InstanceScript* instance = player->GetInstanceScript(); + if (!instance || instance->GetData(DATA_HAD_FAERLINA_GREET) || instance->GetBossState(BOSS_FAERLINA) != NOT_STARTED) + return true; + + if (Creature* faerlina = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_FAERLINA))) + faerlina->AI()->Talk(SAY_GREET); + instance->SetData(DATA_HAD_FAERLINA_GREET, 1u); + + return true; + } +}; + void AddSC_boss_faerlina() { new boss_faerlina(); new npc_faerlina_add(); + new at_faerlina_entrance(); new achievement_momma_said_knock_you_out(); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp index db6a7a77588..494c173f5fc 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp @@ -246,7 +246,7 @@ class spell_loatheb_deathbloom : public SpellScriptLoader void Register() override { - AfterEffectRemove += AuraEffectRemoveFn(spell_loatheb_deathbloom_AuraScript::AfterRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_loatheb_deathbloom_AuraScript::AfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); } }; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp index 7fbf0c913e0..5f9ca92aaf4 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp @@ -172,7 +172,7 @@ public: Talk(SAY_DEATH); } - void DamageTaken(Unit* /*who*/, uint32& damage) // prevent noth from somehow dying in the balcony phase + void DamageTaken(Unit* /*who*/, uint32& damage) override // prevent noth from somehow dying in the balcony phase { if (!events.IsInPhase(PHASE_BALCONY)) return; diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp index f366d0ce201..f0348c408b1 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_FOLLOWER_WORSHIPPER, BOSS_FAERLINA }, { NPC_DK_UNDERSTUDY, BOSS_RAZUVIOUS }, { NPC_SIR, BOSS_HORSEMEN }, { NPC_THANE, BOSS_HORSEMEN }, @@ -126,6 +125,8 @@ class instance_naxxramas : public InstanceMapScript minHorsemenDiedTime = 0; maxHorsemenDiedTime = 0; AbominationCount = 0; + hadAnubRekhanGreet = false; + hadFaerlinaGreet = false; CurrentWingTaunt = SAY_KELTHUZAD_FIRST_WING_TAUNT; playerDied = 0; @@ -135,6 +136,9 @@ class instance_naxxramas : public InstanceMapScript { switch (creature->GetEntry()) { + case NPC_ANUBREKHAN: + AnubRekhanGUID = creature->GetGUID(); + break; case NPC_FAERLINA: FaerlinaGUID = creature->GetGUID(); break; @@ -319,6 +323,14 @@ class instance_naxxramas : public InstanceMapScript case DATA_ABOMINATION_KILLED: AbominationCount = value; break; + case DATA_HAD_ANUBREKHAN_GREET: + hadAnubRekhanGreet = (value == 1u); + break; + case DATA_HAD_FAERLINA_GREET: + hadFaerlinaGreet = (value == 1u); + break; + default: + break; } } @@ -328,6 +340,10 @@ class instance_naxxramas : public InstanceMapScript { case DATA_ABOMINATION_KILLED: return AbominationCount; + case DATA_HAD_ANUBREKHAN_GREET: + return (uint32)hadAnubRekhanGreet; + case DATA_HAD_FAERLINA_GREET: + return (uint32)hadFaerlinaGreet; default: break; } @@ -339,6 +355,8 @@ class instance_naxxramas : public InstanceMapScript { switch (id) { + case DATA_ANUBREKHAN: + return AnubRekhanGUID; case DATA_FAERLINA: return FaerlinaGUID; case DATA_THANE: @@ -599,6 +617,8 @@ class instance_naxxramas : public InstanceMapScript protected: /* The Arachnid Quarter */ + // Anub'rekhan + ObjectGuid AnubRekhanGUID; // Grand Widow Faerlina ObjectGuid FaerlinaGUID; @@ -635,6 +655,8 @@ class instance_naxxramas : public InstanceMapScript ObjectGuid KelthuzadDoorGUID; ObjectGuid LichKingGUID; uint8 AbominationCount; + bool hadAnubRekhanGreet; + bool hadFaerlinaGreet; 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 459903c4c86..6289b707411 100644 --- a/src/server/scripts/Northrend/Naxxramas/naxxramas.h +++ b/src/server/scripts/Northrend/Naxxramas/naxxramas.h @@ -46,6 +46,9 @@ enum Data DATA_HEIGAN_ERUPT, DATA_GOTHIK_GATE, DATA_SAPPHIRON_BIRTH, + DATA_HAD_ANUBREKHAN_GREET, + + DATA_HAD_FAERLINA_GREET, DATA_HORSEMEN0, DATA_HORSEMEN1, @@ -61,6 +64,7 @@ enum Data enum Data64 { + DATA_ANUBREKHAN, DATA_FAERLINA, DATA_THANE, DATA_LADY, @@ -81,6 +85,7 @@ enum Data64 enum CreaturesIds { + NPC_ANUBREKHAN = 15956, NPC_FAERLINA = 15953, NPC_THANE = 16064, NPC_LADY = 16065, diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp index 6233c7e8953..86dbe6c16fb 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp @@ -437,7 +437,7 @@ public: return 0; } - void UpdateEscortAI(const uint32 uiDiff) override + void UpdateEscortAI(uint32 uiDiff) override { if (uiPhaseTimer <= uiDiff) { diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/instance_halls_of_stone.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/instance_halls_of_stone.cpp index c67e31c4cc0..227b9c208cc 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/instance_halls_of_stone.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/instance_halls_of_stone.cpp @@ -18,7 +18,6 @@ #include "InstanceScript.h" #include "Player.h" #include "ScriptMgr.h" -#include "WorldSession.h" #include "halls_of_stone.h" DoorData const doorData[] = @@ -172,7 +171,7 @@ class instance_halls_of_stone : public InstanceMapScript bool CheckRequiredBosses(uint32 bossId, Player const* player = nullptr) const override { - if (player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES)) + if (_SkipCheckRequiredBosses(player)) return true; switch (bossId) diff --git a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp index dc923e534b0..d3868b8df9c 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp @@ -16,11 +16,13 @@ */ #include "ScriptMgr.h" +#include "SpellScript.h" #include "ScriptedCreature.h" #include "violet_hold.h" enum Spells { + SPELL_SUMMON_PLAYER = 21150, SPELL_ARCANE_VACUUM = 58694, SPELL_BLIZZARD = 58693, SPELL_MANA_DESTRUCTION = 59374, @@ -42,119 +44,91 @@ enum Yells class boss_cyanigosa : public CreatureScript { -public: - boss_cyanigosa() : CreatureScript("boss_cyanigosa") { } - - struct boss_cyanigosaAI : public BossAI - { - boss_cyanigosaAI(Creature* creature) : BossAI(creature, DATA_CYANIGOSA) - { - Initialize(); - } - - void Initialize() - { - uiArcaneVacuumTimer = 10000; - uiBlizzardTimer = 15000; - uiManaDestructionTimer = 30000; - uiTailSweepTimer = 20000; - uiUncontrollableEnergyTimer = 25000; - } - - uint32 uiArcaneVacuumTimer; - uint32 uiBlizzardTimer; - uint32 uiManaDestructionTimer; - uint32 uiTailSweepTimer; - uint32 uiUncontrollableEnergyTimer; + public: + boss_cyanigosa() : CreatureScript("boss_cyanigosa") { } - void Reset() override + struct boss_cyanigosaAI : public BossAI { - Initialize(); - BossAI::Reset(); - } + boss_cyanigosaAI(Creature* creature) : BossAI(creature, DATA_CYANIGOSA) { } - void EnterCombat(Unit* who) override - { - BossAI::EnterCombat(who); - Talk(SAY_AGGRO); - } + void EnterCombat(Unit* who) override + { + BossAI::EnterCombat(who); + Talk(SAY_AGGRO); + } - void MoveInLineOfSight(Unit* /*who*/) override { } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } - void UpdateAI(uint32 diff) override - { - if (instance->GetData(DATA_REMOVE_NPC) == 1) + void JustDied(Unit* killer) override { - me->DespawnOrUnsummon(); - instance->SetData(DATA_REMOVE_NPC, 0); + BossAI::JustDied(killer); + Talk(SAY_DEATH); } - if (!UpdateVictim()) - return; + void MoveInLineOfSight(Unit* /*who*/) override { } - if (uiArcaneVacuumTimer <= diff) + void UpdateAI(uint32 diff) override { - DoCastAOE(SPELL_ARCANE_VACUUM); - uiArcaneVacuumTimer = 10000; - } else uiArcaneVacuumTimer -= diff; + if (!UpdateVictim()) + return; - if (uiBlizzardTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_BLIZZARD); - uiBlizzardTimer = 15000; - } else uiBlizzardTimer -= diff; + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } - if (uiTailSweepTimer <= diff) + void ScheduleTasks() override { - DoCastVictim(SPELL_TAIL_SWEEP); - uiTailSweepTimer = 20000; - } else uiTailSweepTimer -= diff; + scheduler.Schedule(Seconds(10), [this](TaskContext task) + { + DoCastAOE(SPELL_ARCANE_VACUUM); + task.Repeat(); + }); - if (uiUncontrollableEnergyTimer <= diff) - { - DoCastVictim(SPELL_UNCONTROLLABLE_ENERGY); - uiUncontrollableEnergyTimer = 25000; - } else uiUncontrollableEnergyTimer -= diff; + scheduler.Schedule(Seconds(15), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true)) + DoCast(target, SPELL_BLIZZARD); + task.Repeat(); + }); - if (IsHeroic()) - { - if (uiManaDestructionTimer <= diff) + scheduler.Schedule(Seconds(20), [this](TaskContext task) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_MANA_DESTRUCTION); - uiManaDestructionTimer = 30000; - } else uiManaDestructionTimer -= diff; - } + DoCastVictim(SPELL_TAIL_SWEEP); + task.Repeat(); + }); - DoMeleeAttackIfReady(); - } + scheduler.Schedule(Seconds(25), [this](TaskContext task) + { + DoCastVictim(SPELL_UNCONTROLLABLE_ENERGY); + task.Repeat(); + }); - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - Talk(SAY_DEATH); - } + if (IsHeroic()) + { + scheduler.Schedule(Seconds(30), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true)) + DoCast(target, SPELL_MANA_DESTRUCTION); + task.Repeat(); + }); + } + } + }; - void KilledUnit(Unit* victim) override + CreatureAI* GetAI(Creature* creature) const override { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); + return GetVioletHoldAI<boss_cyanigosaAI>(creature); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_cyanigosaAI>(creature); - } }; class achievement_defenseless : public AchievementCriteriaScript { public: - achievement_defenseless() : AchievementCriteriaScript("achievement_defenseless") - { - } + achievement_defenseless() : AchievementCriteriaScript("achievement_defenseless") { } bool OnCheck(Player* /*player*/, Unit* target) override { @@ -165,10 +139,40 @@ class achievement_defenseless : public AchievementCriteriaScript if (!instance) return false; - if (!instance->GetData(DATA_DEFENSELESS)) - return false; + return instance->GetData(DATA_DEFENSELESS) != 0; + } +}; + +class spell_cyanigosa_arcane_vacuum : public SpellScriptLoader +{ + public: + spell_cyanigosa_arcane_vacuum() : SpellScriptLoader("spell_cyanigosa_arcane_vacuum") { } - return true; + class spell_cyanigosa_arcane_vacuum_SpellScript : public SpellScript + { + PrepareSpellScript(spell_cyanigosa_arcane_vacuum_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_PLAYER)) + return false; + return true; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_PLAYER, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_cyanigosa_arcane_vacuum_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_cyanigosa_arcane_vacuum_SpellScript(); } }; @@ -176,4 +180,5 @@ void AddSC_boss_cyanigosa() { new boss_cyanigosa(); new achievement_defenseless(); + new spell_cyanigosa_arcane_vacuum(); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp index 8ead8ab559e..cc27bf52118 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp @@ -41,269 +41,210 @@ enum Yells SAY_BOTH_ADDS_KILLED = 5 }; -enum ErekemEvents -{ - EVENT_EARTH_SHIELD = 1, - EVENT_CHAIN_HEAL, - EVENT_BLOODLUST, - EVENT_LIGHTNING_BOLT, - EVENT_EARTH_SHOCK, - EVENT_WINDFURY, - EVENT_STORMSTRIKE -}; - class boss_erekem : public CreatureScript { -public: - boss_erekem() : CreatureScript("boss_erekem") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_erekemAI>(creature); - } - - struct boss_erekemAI : public ScriptedAI - { - boss_erekemAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - phase = 0; - breakBondsCd = 0; - } + public: + boss_erekem() : CreatureScript("boss_erekem") { } - void Reset() override + struct boss_erekemAI : public BossAI { - Initialize(); - - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); - - if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) + boss_erekemAI(Creature* creature) : BossAI(creature, DATA_EREKEM) { - if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) - pGuard1->DespawnOrUnsummon(); - if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) - pGuard2->DespawnOrUnsummon(); + Initialize(); } - else + + void Initialize() { - if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) - { - if (!pGuard1->IsAlive()) - pGuard1->Respawn(); - } - if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) - { - if (!pGuard2->IsAlive()) - pGuard2->Respawn(); - } + _phase = 0; } - events.Reset(); - } - - void JustReachedHome() override - { - if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) - pGuard1->Respawn(); + void Reset() override + { + Initialize(); + BossAI::Reset(); + me->SetCanDualWield(false); + } - if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) - pGuard2->Respawn(); - } + void EnterCombat(Unit* who) override + { + BossAI::EnterCombat(who); + Talk(SAY_AGGRO); + DoCast(me, SPELL_EARTH_SHIELD); + } - void AttackStart(Unit* who) override - { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; + void MovementInform(uint32 type, uint32 pointId) override + { + if (type == EFFECT_MOTION_TYPE && pointId == POINT_INTRO) + me->SetFacingTo(4.921828f); + } - if (me->Attack(who, true)) + void JustReachedHome() override { - me->AddThreat(who, 0.0f); - me->SetInCombatWith(who); - who->SetInCombatWith(me); - DoStartMovement(who); + BossAI::JustReachedHome(); + instance->SetData(DATA_HANDLE_CELLS, DATA_EREKEM); + } - if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) - { - pGuard1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - if (!pGuard1->GetVictim() && pGuard1->AI()) - pGuard1->AI()->AttackStart(who); - } - if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) - { - pGuard2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - if (!pGuard2->GetVictim() && pGuard2->AI()) - pGuard2->AI()->AttackStart(who); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); } - } - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - DoCast(me, SPELL_EARTH_SHIELD); + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } - if (GameObject* door = instance->GetGameObject(DATA_EREKEM_CELL)) - if (door->GetGoState() == GO_STATE_READY) + bool CheckGuardAuras(Creature* guard) const + { + static uint32 const MechanicImmunityList = + (1 << MECHANIC_SNARE) + | (1 << MECHANIC_ROOT) + | (1 << MECHANIC_FEAR) + | (1 << MECHANIC_STUN) + | (1 << MECHANIC_SLEEP) + | (1 << MECHANIC_CHARM) + | (1 << MECHANIC_SAPPED) + | (1 << MECHANIC_HORROR) + | (1 << MECHANIC_POLYMORPH) + | (1 << MECHANIC_DISORIENTED) + | (1 << MECHANIC_FREEZE) + | (1 << MECHANIC_TURN); + + static std::list<AuraType> const AuraImmunityList = { - EnterEvadeMode(); - return; - } + SPELL_AURA_MOD_STUN, + SPELL_AURA_MOD_DECREASE_SPEED, + SPELL_AURA_MOD_ROOT, + SPELL_AURA_MOD_CONFUSE, + SPELL_AURA_MOD_FEAR + }; - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + if (guard->HasAuraWithMechanic(MechanicImmunityList)) + return true; - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); - - events.ScheduleEvent(EVENT_EARTH_SHIELD, 20000); - events.ScheduleEvent(EVENT_BLOODLUST, 15000); - events.ScheduleEvent(EVENT_CHAIN_HEAL, 10000); - events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 2000); - } + for (AuraType type : AuraImmunityList) + if (guard->HasAuraType(type)) + return true; - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - - if (instance->GetData(DATA_WAVE_COUNT) == 6) - { - instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 7); + return false; } - else if (instance->GetData(DATA_WAVE_COUNT) == 12) + + bool CheckGuardAlive() const { - instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 13); - } - } + for (uint32 i = DATA_EREKEM_GUARD_1; i <= DATA_EREKEM_GUARD_2; ++i) + { + if (Creature* guard = ObjectAccessor::GetCreature(*me, instance->GetGuidData(i))) + if (guard->IsAlive()) + return true; + } - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } + return false; + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + Unit* GetChainHealTarget() const + { + if (HealthBelowPct(85)) + return me; - if (phase == 0) - if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) + for (uint32 i = DATA_EREKEM_GUARD_1; i <= DATA_EREKEM_GUARD_2; ++i) { - if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) - { - if (!pGuard1->IsAlive() && !pGuard2->IsAlive()) - { - phase = 1; - DoCastVictim(SPELL_STORMSTRIKE); - DoCast(SPELL_WINDFURY); - events.Reset(); - events.ScheduleEvent(EVENT_EARTH_SHOCK, urand(2000, 8000)); - events.ScheduleEvent(EVENT_WINDFURY, urand(1500, 2000)); - events.ScheduleEvent(EVENT_STORMSTRIKE, urand(1500, 2000)); - } - } + if (Creature* guard = ObjectAccessor::GetCreature(*me, instance->GetGuidData(i))) + if (guard->IsAlive() && !guard->HealthAbovePct(75)) + return guard; } - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + return nullptr; + } - if (breakBondsCd <= 0) + void UpdateAI(uint32 diff) override { - if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) + if (!UpdateVictim()) + return; + + if (_phase == 0 && !CheckGuardAlive()) { - if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) - { - if (pGuard1->IsAlive()) - { - if (pGuard1->HasAuraType(SPELL_AURA_MOD_STUN) || pGuard1->HasAuraType(SPELL_AURA_MOD_ROOT) - || pGuard1->HasAuraType(SPELL_AURA_MOD_CONFUSE) || pGuard1->HasAuraType(SPELL_AURA_MOD_PACIFY) - || pGuard1->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) - { - DoCast(SPELL_BREAK_BONDS); - breakBondsCd = 10000; - return; - } - } - if (pGuard2->IsAlive()) - { - if (pGuard2->HasAuraType(SPELL_AURA_MOD_STUN) || pGuard2->HasAuraType(SPELL_AURA_MOD_ROOT) - || pGuard2->HasAuraType(SPELL_AURA_MOD_CONFUSE) || pGuard2->HasAuraType(SPELL_AURA_MOD_PACIFY) - || pGuard2->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) - { - DoCast(SPELL_BREAK_BONDS); - breakBondsCd = 10000; - return; - } - } - } + _phase = 1; + me->SetCanDualWield(true); + DoCast(me, SPELL_WINDFURY, true); } + + scheduler.Update(diff, [this] + { + if (_phase == 1) + DoSpellAttackIfReady(SPELL_STORMSTRIKE); + else + DoMeleeAttackIfReady(); + }); } - else - breakBondsCd -= diff; - switch (events.ExecuteEvent()) + void ScheduleTasks() override { - case EVENT_EARTH_SHIELD: + scheduler.Schedule(Seconds(20), [this](TaskContext task) + { if (Unit* ally = DoSelectLowestHpFriendly(30.0f)) DoCast(ally, SPELL_EARTH_SHIELD); - events.ScheduleEvent(EVENT_EARTH_SHIELD, 20000); - break; - case EVENT_BLOODLUST: + + task.Repeat(Seconds(20)); + }); + + scheduler.Schedule(Seconds(2), [this](TaskContext task) + { DoCast(SPELL_BLOODLUST); - events.ScheduleEvent(EVENT_BLOODLUST, urand(35000, 45000)); - break; - case EVENT_LIGHTNING_BOLT: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + task.Repeat(Seconds(35), Seconds(45)); + }); + + scheduler.Schedule(Seconds(2), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f)) DoCast(target, SPELL_LIGHTNING_BOLT); - events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 2500); - break; - case EVENT_CHAIN_HEAL: + + task.Repeat(Milliseconds(2500)); + }); + + scheduler.Schedule(Seconds(10), [this](TaskContext task) + { if (Unit* ally = DoSelectLowestHpFriendly(40.0f)) DoCast(ally, SPELL_CHAIN_HEAL); + + if (!CheckGuardAlive()) + task.Repeat(Seconds(3)); + else + task.Repeat(Seconds(8), Seconds(11)); + }); + + scheduler.Schedule(Seconds(2), Seconds(8), [this](TaskContext task) + { + DoCastVictim(SPELL_EARTH_SHOCK); + task.Repeat(Seconds(8), Seconds(13)); + }); + + scheduler.Schedule(Seconds(0), [this](TaskContext task) + { + for (uint32 i = DATA_EREKEM_GUARD_1; i <= DATA_EREKEM_GUARD_2; ++i) { - Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)); - Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)); - events.ScheduleEvent(EVENT_CHAIN_HEAL, ((pGuard1 && !pGuard1->IsAlive()) || (pGuard2 && !pGuard2->IsAlive()) ? 3000 : 8000 + rand() % 3000)); + Creature* guard = ObjectAccessor::GetCreature(*me, instance->GetGuidData(i)); + + if (guard && guard->IsAlive() && CheckGuardAuras(guard)) + { + DoCastAOE(SPELL_BREAK_BONDS); + task.Repeat(Seconds(10)); + return; + } } - break; - case EVENT_EARTH_SHOCK: - DoCastVictim(SPELL_EARTH_SHOCK); - events.ScheduleEvent(EVENT_EARTH_SHOCK, urand(8000, 13000)); - break; - case EVENT_WINDFURY: - DoCast(SPELL_WINDFURY); - events.ScheduleEvent(EVENT_WINDFURY, urand(1500, 2000)); - break; - case EVENT_STORMSTRIKE: - DoCastVictim(SPELL_STORMSTRIKE); - events.ScheduleEvent(EVENT_STORMSTRIKE, urand(1500, 2000)); - break; - default: - break; + task.Repeat(Milliseconds(500)); + }); } - DoMeleeAttackIfReady(); - } + private: + uint8 _phase; + }; - private: - EventMap events; - InstanceScript* instance; - uint8 phase; - int32 breakBondsCd; - }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<boss_erekemAI>(creature); + } }; enum GuardSpells @@ -315,85 +256,61 @@ enum GuardSpells class npc_erekem_guard : public CreatureScript { -public: - npc_erekem_guard() : CreatureScript("npc_erekem_guard") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_erekem_guardAI>(creature); - } + public: + npc_erekem_guard() : CreatureScript("npc_erekem_guard") { } - struct npc_erekem_guardAI : public ScriptedAI - { - npc_erekem_guardAI(Creature* creature) : ScriptedAI(creature) + struct npc_erekem_guardAI : public ScriptedAI { - Initialize(); - instance = creature->GetInstanceScript(); - } + npc_erekem_guardAI(Creature* creature) : ScriptedAI(creature) { } - void Initialize() - { - uiStrikeTimer = urand(4000, 8000); - uiHowlingScreechTimer = urand(8000, 13000); - uiGushingWoundTimer = urand(1000, 3000); - } + void Reset() override + { + scheduler.CancelAll(); + } - uint32 uiGushingWoundTimer; - uint32 uiHowlingScreechTimer; - uint32 uiStrikeTimer; + void EnterCombat(Unit* /*who*/) override + { + DoZoneInCombat(); + } - InstanceScript* instance; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - void Reset() override - { - Initialize(); + scheduler.Update(diff, + std::bind(&ScriptedAI::DoMeleeAttackIfReady, this)); + } - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); - } + void ScheduledTasks() + { + scheduler.Schedule(Seconds(4), Seconds(8), [this](TaskContext task) + { + DoCastVictim(SPELL_STRIKE); + task.Repeat(Seconds(4), Seconds(8)); + }); - void AttackStart(Unit* who) override - { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; + scheduler.Schedule(Seconds(8), Seconds(13), [this](TaskContext task) + { + DoCastAOE(SPELL_HOWLING_SCREECH); + task.Repeat(Seconds(8), Seconds(13)); + }); - if (me->Attack(who, true)) - { - me->AddThreat(who, 0.0f); - me->SetInCombatWith(who); - who->SetInCombatWith(me); - DoStartMovement(who); + scheduler.Schedule(Seconds(1), Seconds(3), [this](TaskContext task) + { + DoCastVictim(SPELL_GUSHING_WOUND); + task.Repeat(Seconds(7), Seconds(12)); + }); } - } - void MoveInLineOfSight(Unit* /*who*/) override { } + private: + TaskScheduler scheduler; + }; - void UpdateAI(uint32 diff) override + CreatureAI* GetAI(Creature* creature) const override { - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); - - if (uiStrikeTimer <= diff) - { - DoCastVictim(SPELL_STRIKE); - uiStrikeTimer = urand(4000, 8000); - } else uiStrikeTimer -= diff; - - if (uiHowlingScreechTimer <= diff) - { - DoCastVictim(SPELL_HOWLING_SCREECH); - uiHowlingScreechTimer = urand(8000, 13000); - } else uiHowlingScreechTimer -= diff; - - if (uiGushingWoundTimer <= diff) - { - DoCastVictim(SPELL_GUSHING_WOUND); - uiGushingWoundTimer = urand(7000, 12000); - } else uiGushingWoundTimer -= diff; + return GetVioletHoldAI<npc_erekem_guardAI>(creature); } - }; }; void AddSC_boss_erekem() diff --git a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp index caf1392ea38..3c29cc1123c 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp @@ -17,26 +17,32 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellAuraEffects.h" +#include "SpellScript.h" #include "violet_hold.h" enum Spells { - SPELL_DRAINED = 59820, - SPELL_FRENZY = 54312, - SPELL_PROTECTIVE_BUBBLE = 54306, SPELL_WATER_BLAST = 54237, SPELL_WATER_BOLT_VOLLEY = 54241, - SPELL_SPLASH = 59516, + SPELL_SPLATTER = 54259, + SPELL_PROTECTIVE_BUBBLE = 54306, + SPELL_FRENZY = 54312, SPELL_BURST = 54379, - SPELL_WATER_GLOBULE = 54268, - SPELL_MERGE = 54269, - SPELL_WATER_GLOBULE_VISUAL = 54260 -}; + SPELL_DRAINED = 59820, + SPELL_THREAT_PROC = 61732, + SPELL_SHRINK = 54297, -enum IchoronCreatures -{ - NPC_ICHOR_GLOBULE = 29321, - NPC_ICHORON_SUMMON_TARGET = 29326 + SPELL_WATER_GLOBULE_SUMMON_1 = 54258, + SPELL_WATER_GLOBULE_SUMMON_2 = 54264, + SPELL_WATER_GLOBULE_SUMMON_3 = 54265, + SPELL_WATER_GLOBULE_SUMMON_4 = 54266, + SPELL_WATER_GLOBULE_SUMMON_5 = 54267, + SPELL_WATER_GLOBULE_TRANSFORM = 54268, + SPELL_WATER_GLOBULE_VISUAL = 54260, + + SPELL_MERGE = 54269, + SPELL_SPLASH = 59516 }; enum Yells @@ -47,483 +53,412 @@ enum Yells SAY_SPAWN = 3, SAY_ENRAGE = 4, SAY_SHATTER = 5, - SAY_BUBBLE = 6 + SAY_BUBBLE = 6, + EMOTE_SHATTER = 7 }; enum Actions { - ACTION_WATER_ELEMENT_HIT = 1 -}; - -enum IchoronEvents -{ - EVENT_WATER_BLAST = 1, - EVENT_WATER_BOLT_VOLLEY -}; - -enum GlobuleEvents -{ - EVENT_GLOBULE_MOVE = 1 + ACTION_WATER_GLOBULE_HIT = 1, + ACTION_PROTECTIVE_BUBBLE_SHATTERED = 2, + ACTION_DRAINED = 3 }; enum Misc { - DATA_GLOBULE_PATH = 0, DATA_DEHYDRATION = 1 }; - -#define MAX_GLOBULE_PATHS 10 - -Position const globulePaths[MAX_GLOBULE_PATHS] = -{ - // first target - { 1861.357f, 804.039f, 44.008f, 6.268f }, - { 1869.375f, 803.976f, 38.781f, 0.009f }, - // second target - { 1888.063f, 763.488f, 47.667f, 1.744f }, - { 1882.865f, 776.385f, 38.824f, 1.882f }, - // third target - { 1935.140f, 817.752f, 52.181f, 1.885f }, - { 1916.642f, 826.337f, 39.139f, 2.851f }, - // fourth target - { 1930.257f, 833.053f, 46.906f, 4.579f }, - { 1916.642f, 826.337f, 39.139f, 2.851f }, - // fifth target - { 1878.248f, 841.883f, 43.334f, 4.717f }, - { 1879.438f, 834.443f, 38.699f, 4.831f } -}; - class boss_ichoron : public CreatureScript { -public: - boss_ichoron() : CreatureScript("boss_ichoron") { } + public: + boss_ichoron() : CreatureScript("boss_ichoron") { } - struct boss_ichoronAI : public ScriptedAI - { - boss_ichoronAI(Creature* creature) : ScriptedAI(creature), m_waterElements(creature) + struct boss_ichoronAI : public BossAI { - Initialize(); - instance = creature->GetInstanceScript(); - } + boss_ichoronAI(Creature* creature) : BossAI(creature, DATA_ICHORON) + { + Initialize(); - void Initialize() - { - bIsExploded = false; - bIsFrenzy = false; - bIsDrained = false; - dehydration = true; - drainedTimer = 50; - burstTimer = 15000; - } + /// for some reason ichoron can't walk back to it's water basin on evade + me->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); + } - void Reset() override - { - Initialize(); + void Initialize() + { + _isFrenzy = false; + _dehydration = true; + } - events.Reset(); - me->SetVisible(true); - DespawnWaterElements(); + void Reset() override + { + Initialize(); + BossAI::Reset(); - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); - } + DoCast(me, SPELL_THREAT_PROC, true); + } - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_AGGRO); + void EnterCombat(Unit* who) override + { + BossAI::EnterCombat(who); + Talk(SAY_AGGRO); + } - DoCast(me, SPELL_PROTECTIVE_BUBBLE); + void JustReachedHome() override + { + BossAI::JustReachedHome(); + instance->SetData(DATA_HANDLE_CELLS, DATA_ICHORON); + } - if (GameObject* door = instance->GetGameObject(DATA_ICHORON_CELL)) - if (door->GetGoState() == GO_STATE_READY) + void DoAction(int32 actionId) override + { + switch (actionId) { - EnterEvadeMode(); - return; - } + case ACTION_WATER_GLOBULE_HIT: + if (!me->IsAlive()) + break; - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + me->ModifyHealth(int32(me->CountPctFromMaxHealth(3))); + _dehydration = false; + break; + case ACTION_PROTECTIVE_BUBBLE_SHATTERED: + { + Talk(SAY_SHATTER); + Talk(EMOTE_SHATTER); - events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, urand(10000, 15000)); - events.ScheduleEvent(EVENT_WATER_BLAST, urand(6000, 9000)); - } + DoCastAOE(SPELL_SPLATTER, true); + DoCastAOE(SPELL_BURST, true); + DoCast(me, SPELL_DRAINED, true); - void AttackStart(Unit* who) override - { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; + uint32 damage = me->CountPctFromMaxHealth(30); + me->LowerPlayerDamageReq(damage); + me->ModifyHealth(-std::min<int32>(damage, me->GetHealth() - 1)); - if (me->Attack(who, true)) - { - me->AddThreat(who, 0.0f); - me->SetInCombatWith(who); - who->SetInCombatWith(me); - DoStartMovement(who); + scheduler.DelayAll(Seconds(15)); + break; + } + case ACTION_DRAINED: + if (HealthAbovePct(30)) + { + Talk(SAY_BUBBLE); + DoCast(me, SPELL_PROTECTIVE_BUBBLE, true); + } + break; + default: + break; + } } - } - void DoAction(int32 param) override - { - if (!me->IsAlive()) - return; + uint32 GetData(uint32 type) const override + { + if (type == DATA_DEHYDRATION) + return _dehydration ? 1 : 0; + return 0; + } - switch (param) + void KilledUnit(Unit* victim) override { - case ACTION_WATER_ELEMENT_HIT: - { - if (bIsExploded) - DoExplodeCompleted(); + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } - me->SetHealth(me->GetHealth() + me->CountPctFromMaxHealth(3)); - dehydration = false; - } - break; + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); } - } - void DespawnWaterElements() - { - m_waterElements.DespawnAll(); - } + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); - // call when explode shall stop. - // either when "hit" by a bubble, or when there is no bubble left. - void DoExplodeCompleted() - { - bIsExploded = false; - bIsDrained = false; + if (summon->GetEntry() == NPC_ICHOR_GLOBULE) + DoCast(summon, SPELL_WATER_GLOBULE_VISUAL); + } - if (!HealthBelowPct(25)) + void SummonedCreatureDespawn(Creature* summon) override { - Talk(SAY_BUBBLE); - DoCast(me, SPELL_PROTECTIVE_BUBBLE, true); + BossAI::SummonedCreatureDespawn(summon); + + if (summons.empty()) + me->RemoveAurasDueToSpell(SPELL_DRAINED, ObjectGuid::Empty, 0, AURA_REMOVE_BY_EXPIRE); } - me->SetVisible(true); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - uint32 GetData(uint32 type) const override - { - if (type == DATA_DEHYDRATION) - return dehydration ? 1 : 0; + if (!_isFrenzy && HealthBelowPct(25) && !me->HasAura(SPELL_DRAINED)) + { + Talk(SAY_ENRAGE); + DoCast(me, SPELL_FRENZY, true); + _isFrenzy = true; + } - return 0; - } + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } - void MoveInLineOfSight(Unit* who) override - { - if (!who->ToCreature()) - return; + void ScheduleTasks() override + { + scheduler.Async([this] + { + DoCast(me, SPELL_SHRINK); + DoCast(me, SPELL_PROTECTIVE_BUBBLE); + }); - if (who->GetEntry() != NPC_ICHOR_GLOBULE) - return; + scheduler.Schedule(Seconds(10), Seconds(15), [this](TaskContext task) + { + DoCastAOE(SPELL_WATER_BOLT_VOLLEY); + task.Repeat(Seconds(10), Seconds(15)); + }); - if (!me->IsWithinDist(who, 4.0f, false)) - return; + scheduler.Schedule(Seconds(6), Seconds(9), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f)) + DoCast(target, SPELL_WATER_BLAST); + task.Repeat(Seconds(6), Seconds(9)); + }); + } - if (who->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - return; + private: + bool _isFrenzy; + bool _dehydration; + }; - who->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - who->CastSpell(who, SPELL_MERGE); - DoAction(ACTION_WATER_ELEMENT_HIT); - who->ToCreature()->DespawnOrUnsummon(1000); + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<boss_ichoronAI>(creature); } +}; - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); +class npc_ichor_globule : public CreatureScript +{ + public: + npc_ichor_globule() : CreatureScript("npc_ichor_globule") { } - if (bIsExploded) + struct npc_ichor_globuleAI : public ScriptedAI + { + npc_ichor_globuleAI(Creature* creature) : ScriptedAI(creature) { - bIsExploded = false; - me->SetVisible(true); + _instance = creature->GetInstanceScript(); + creature->SetReactState(REACT_PASSIVE); } - DespawnWaterElements(); - - if (instance->GetData(DATA_WAVE_COUNT) == 6) - { - instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 7); - } - else if (instance->GetData(DATA_WAVE_COUNT) == 12) + void SpellHit(Unit* caster, SpellInfo const* spellInfo) override { - instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 13); + if (spellInfo->Id == SPELL_WATER_GLOBULE_VISUAL) + { + DoCast(me, SPELL_WATER_GLOBULE_TRANSFORM); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->GetMotionMaster()->MoveFollow(caster, 0.0f, 0.0f); + } } - } - void JustSummoned(Creature* summoned) override - { - summoned->SetSpeed(MOVE_RUN, 0.3f); - m_waterElements.Summon(summoned); + void MovementInform(uint32 type, uint32 id) override + { + if (type != FOLLOW_MOTION_TYPE) + return; - instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); - } + if (_instance->GetObjectGuid(DATA_ICHORON).GetCounter() != id) + return; - void SummonedCreatureDespawn(Creature* summoned) override - { - m_waterElements.Despawn(summoned); + me->CastSpell(me, SPELL_MERGE); + me->DespawnOrUnsummon(1); + } - if (m_waterElements.empty() && bIsExploded) + // on retail spell casted on a creature's death are not casted after death but keeping mob at 1 health, casting it and then letting the mob die. + // this feature should be still implemented + void DamageTaken(Unit* /*attacker*/, uint32& damage) override { - me->RemoveAllAuras(); - DoExplodeCompleted(); + if (damage >= me->GetHealth()) + DoCastAOE(SPELL_SPLASH); } - instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID()); - } + void UpdateAI(uint32 /*diff*/) override { } + + private: + InstanceScript* _instance; + }; - void KilledUnit(Unit* victim) override + CreatureAI* GetAI(Creature* creature) const override { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); + return GetVioletHoldAI<npc_ichor_globuleAI>(creature); } +}; + +// 59820 - Drained +class spell_ichoron_drained : public SpellScriptLoader +{ + public: + spell_ichoron_drained() : SpellScriptLoader("spell_ichoron_drained") { } - void UpdateAI(uint32 diff) override + class spell_ichoron_drained_AuraScript : public AuraScript { - if (!UpdateVictim()) - return; + PrepareAuraScript(spell_ichoron_drained_AuraScript); - if (!bIsFrenzy && HealthBelowPct(25) && !bIsExploded) + bool Load() override { - Talk(SAY_ENRAGE); - DoCast(me, SPELL_FRENZY, true); - bIsFrenzy = true; + return GetOwner()->GetEntry() == NPC_ICHORON || GetOwner()->GetEntry() == NPC_DUMMY_ICHORON; } - if (!bIsFrenzy) + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (!bIsExploded) - { - if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE)) - { - bIsExploded = true; - Talk(SAY_SHATTER); - DoCast(SPELL_BURST); - me->RemoveAllAuras(); - burstTimer = 15000; + GetTarget()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_UNK_31); + GetTarget()->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); + } - std::list<Creature*> summonTargets; - GetCreatureListWithEntryInGrid(summonTargets, me, NPC_ICHORON_SUMMON_TARGET, 200.0f); - std::list<Creature*>::iterator itr = summonTargets.begin(); + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_UNK_31); + GetTarget()->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); - for (uint8 i = 0; i < MAX_GLOBULE_PATHS; i++) - { - std::advance(itr, urand(0, summonTargets.size() - 1)); // I take a random minion in the list - Position targetPos = (*itr)->GetRandomNearPosition(10.0f); - itr = summonTargets.begin(); - TempSummon* globule = me->SummonCreature(NPC_ICHOR_GLOBULE, targetPos, TEMPSUMMON_CORPSE_DESPAWN); - DoCast(globule, SPELL_WATER_GLOBULE_VISUAL); - - float minDistance = 1000.0f; - uint8 nextPath = 0; - // I move the globules to next position. the 10 positions are in couples, defined in globulePaths, so i have to increase by 2. - for (uint8 gpath = 0; gpath < MAX_GLOBULE_PATHS; gpath += 2) - { - if (globule->GetDistance(globulePaths[gpath]) < minDistance) - { - minDistance = globule->GetDistance(globulePaths[gpath]); - nextPath = gpath; - } - } - - globule->GetAI()->SetData(DATA_GLOBULE_PATH, nextPath); - } - return; - } + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) + if (GetTarget()->IsAIEnabled) + GetTarget()->GetAI()->DoAction(ACTION_DRAINED); + } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_ichoron_drained_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_ichoron_drained_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + } + }; - events.Update(diff); + AuraScript* GetAuraScript() const override + { + return new spell_ichoron_drained_AuraScript(); + } +}; - switch (events.ExecuteEvent()) - { - case EVENT_WATER_BLAST: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_WATER_BLAST); - events.ScheduleEvent(EVENT_WATER_BLAST, urand(6000, 9000)); - break; - case EVENT_WATER_BOLT_VOLLEY: - DoCast(SPELL_WATER_BOLT_VOLLEY); - events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, urand(10000, 15000)); - break; - } +// 54269 - Merge +class spell_ichoron_merge : public SpellScriptLoader +{ + public: + spell_ichoron_merge() : SpellScriptLoader("spell_ichoron_merge") { } - DoMeleeAttackIfReady(); - } - else if (!bIsDrained) - { - if (drainedTimer <= 0) - { - bIsDrained = true; - drainedTimer = 50; - uint32 damage = me->CountPctFromMaxHealth(30); - if (me->GetHealth() < damage) - me->SetHealth(me->CountPctFromMaxHealth(1)); - else - { - me->SetHealth(me->GetHealth() - damage); - me->LowerPlayerDamageReq(damage); - } - DoCast(SPELL_DRAINED); - me->SetVisible(false); - me->AttackStop(); - } - else - drainedTimer -= diff; - } - else if (bIsDrained) - { - if (burstTimer <= 0) - { - DoExplodeCompleted(); - } - else - burstTimer -= diff; - } - } - else - { - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + class spell_ichoron_merge_SpellScript : public SpellScript + { + PrepareSpellScript(spell_ichoron_merge_SpellScript); - events.Update(diff); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHRINK)) + return false; + return true; + } - switch (events.ExecuteEvent()) + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Creature* target = GetHitCreature()) { - case EVENT_WATER_BLAST: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_WATER_BLAST); - events.ScheduleEvent(EVENT_WATER_BLAST, urand(6000, 9000)); - break; - case EVENT_WATER_BOLT_VOLLEY: - DoCast(SPELL_WATER_BOLT_VOLLEY); - events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, urand(10000, 15000)); - break; + if (Aura* aura = target->GetAura(SPELL_SHRINK)) + aura->ModStackAmount(-1); + + target->AI()->DoAction(ACTION_WATER_GLOBULE_HIT); } + } - DoMeleeAttackIfReady(); + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_ichoron_merge_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } - } + }; - private: - InstanceScript* instance; - SummonList m_waterElements; - EventMap events; - bool bIsExploded; - bool bIsFrenzy; - bool bIsDrained; - bool dehydration; - int32 drainedTimer; - int32 burstTimer; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_ichoronAI>(creature); - } + SpellScript* GetSpellScript() const override + { + return new spell_ichoron_merge_SpellScript(); + } }; -class npc_ichor_globule : public CreatureScript +// 54306 - Protective Bubble +class spell_ichoron_protective_bubble : public SpellScriptLoader { -public: - npc_ichor_globule() : CreatureScript("npc_ichor_globule") { } + public: + spell_ichoron_protective_bubble() : SpellScriptLoader("spell_ichoron_protective_bubble") { } - struct npc_ichor_globuleAI : public ScriptedAI - { - npc_ichor_globuleAI(Creature* creature) : ScriptedAI(creature) + class spell_ichoron_protective_bubble_AuraScript : public AuraScript { - Initialize(); - instance = creature->GetInstanceScript(); - } + PrepareAuraScript(spell_ichoron_protective_bubble_AuraScript); - void Initialize() - { - pathId = 0; - } + bool Load() override + { + return GetOwner()->GetEntry() == NPC_ICHORON || GetOwner()->GetEntry() == NPC_DUMMY_ICHORON; + } - void Reset() override - { - Initialize(); - events.Reset(); - DoCast(SPELL_WATER_GLOBULE); - me->SetReactState(REACT_PASSIVE); - } + void HandleShatter(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + //if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_ENEMY_SPELL) + if (GetAura()->GetCharges() <= 1) + if (GetTarget()->IsAIEnabled) + GetTarget()->GetAI()->DoAction(ACTION_PROTECTIVE_BUBBLE_SHATTERED); + } - void SetData(uint32 id, uint32 data) override - { - if (id == DATA_GLOBULE_PATH) + void Register() override { - pathId = data; - me->GetMotionMaster()->MovePoint(0, globulePaths[pathId]); + AfterEffectRemove += AuraEffectRemoveFn(spell_ichoron_protective_bubble_AuraScript::HandleShatter, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_ichoron_protective_bubble_AuraScript(); } +}; - void MovementInform(uint32 type, uint32 id) override +// 54259 - Splatter +class spell_ichoron_splatter : public SpellScriptLoader +{ + public: + spell_ichoron_splatter() : SpellScriptLoader("spell_ichoron_splatter") { } + + class spell_ichoron_splatter_AuraScript : public AuraScript { - if (type != POINT_MOTION_TYPE) - return; + PrepareAuraScript(spell_ichoron_splatter_AuraScript); - switch (id) + bool Validate(SpellInfo const* /*spellInfo*/) override { - case 0: - me->GetMotionMaster()->Clear(); - events.ScheduleEvent(EVENT_GLOBULE_MOVE, 500); - break; - case 1: - me->GetMotionMaster()->Clear(); - if (Creature* ichoron = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_ICHORON))) - me->GetMotionMaster()->MoveFollow(ichoron, 0.0f, 0.0f); - break; + if (!sSpellMgr->GetSpellInfo(SPELL_WATER_GLOBULE_SUMMON_1) + || !sSpellMgr->GetSpellInfo(SPELL_WATER_GLOBULE_SUMMON_2) + || !sSpellMgr->GetSpellInfo(SPELL_WATER_GLOBULE_SUMMON_3) + || !sSpellMgr->GetSpellInfo(SPELL_WATER_GLOBULE_SUMMON_4) + || !sSpellMgr->GetSpellInfo(SPELL_WATER_GLOBULE_SUMMON_5) + || !sSpellMgr->GetSpellInfo(SPELL_SHRINK)) + return false; + return true; } - } - // on retail spell casted on a creature's death are not casted after death but keeping mob at 1 health, casting it and then letting the mob die. - // this feature should be still implemented - void DamageTaken(Unit* /*attacker*/, uint32 &damage) override - { - int32 actualHp = me->GetHealth(); - actualHp -= damage; + void PeriodicTick(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), RAND(SPELL_WATER_GLOBULE_SUMMON_1, SPELL_WATER_GLOBULE_SUMMON_2, SPELL_WATER_GLOBULE_SUMMON_3, SPELL_WATER_GLOBULE_SUMMON_4, SPELL_WATER_GLOBULE_SUMMON_5), true); + } - if (actualHp <= 0) - DoCast(SPELL_SPLASH); - } + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) + if (Aura* aura = GetTarget()->GetAura(SPELL_SHRINK)) + aura->ModStackAmount(10); + } - void UpdateAI(uint32 diff) override - { - events.Update(diff); + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_ichoron_splatter_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + AfterEffectRemove += AuraEffectRemoveFn(spell_ichoron_splatter_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; - if (events.ExecuteEvent() == EVENT_GLOBULE_MOVE) - me->GetMotionMaster()->MovePoint(1, globulePaths[pathId + 1]); + AuraScript* GetAuraScript() const override + { + return new spell_ichoron_splatter_AuraScript(); } - - private: - InstanceScript* instance; - EventMap events; - uint8 pathId; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_ichor_globuleAI>(creature); - } }; class achievement_dehydration : public AchievementCriteriaScript { public: - achievement_dehydration() : AchievementCriteriaScript("achievement_dehydration") - { - } + achievement_dehydration() : AchievementCriteriaScript("achievement_dehydration") { } bool OnCheck(Player* /*player*/, Unit* target) override { @@ -542,5 +477,9 @@ void AddSC_boss_ichoron() { new boss_ichoron(); new npc_ichor_globule(); + new spell_ichoron_drained(); + new spell_ichoron_merge(); + new spell_ichoron_protective_bubble(); + new spell_ichoron_splatter(); new achievement_dehydration(); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp index 8b77b512ca4..c3b617f8199 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp @@ -27,130 +27,82 @@ enum Spells SPELL_LAVA_BURN = 54249 }; -enum LavanthorEvents -{ - EVENT_CAUTERIZING_FLAMES = 1, - EVENT_FIREBOLT, - EVENT_FLAME_BREATH, - EVENT_LAVA_BURN -}; - class boss_lavanthor : public CreatureScript { -public: - boss_lavanthor() : CreatureScript("boss_lavanthor") { } + public: + boss_lavanthor() : CreatureScript("boss_lavanthor") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_lavanthorAI>(creature); - } - - struct boss_lavanthorAI : public ScriptedAI - { - boss_lavanthorAI(Creature* creature) : ScriptedAI(creature) + struct boss_lavanthorAI : public BossAI { - instance = creature->GetInstanceScript(); - } + boss_lavanthorAI(Creature* creature) : BossAI(creature, DATA_LAVANTHOR) { } - void Reset() override - { - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); - - events.Reset(); - } - - void EnterCombat(Unit* /*who*/) override - { - if (GameObject* door = instance->GetGameObject(DATA_LAVANTHOR_CELL)) - if (door->GetGoState() == GO_STATE_READY) - { - EnterEvadeMode(); - return; - } - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); - - events.ScheduleEvent(EVENT_FIREBOLT, 1000); - events.ScheduleEvent(EVENT_FLAME_BREATH, 5000); - events.ScheduleEvent(EVENT_LAVA_BURN, 10000); - if (IsHeroic()) - events.ScheduleEvent(EVENT_CAUTERIZING_FLAMES, 3000); - } + void Reset() override + { + BossAI::Reset(); + } - void AttackStart(Unit* who) override - { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; + void EnterCombat(Unit* who) override + { + BossAI::EnterCombat(who); + } - if (me->Attack(who, true)) + void JustReachedHome() override { - me->AddThreat(who, 0.0f); - me->SetInCombatWith(who); - who->SetInCombatWith(me); - DoStartMovement(who); + BossAI::JustReachedHome(); + instance->SetData(DATA_HANDLE_CELLS, DATA_LAVANTHOR); } - } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + } - events.Update(diff); + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } - switch (events.ExecuteEvent()) + void ScheduleTasks() override { - case EVENT_FIREBOLT: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + scheduler.Schedule(Seconds(1), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f, true)) DoCast(target, SPELL_FIREBOLT); - events.ScheduleEvent(EVENT_FIREBOLT, urand(5000, 13000)); - break; - case EVENT_FLAME_BREATH: - DoCast(SPELL_FLAME_BREATH); - events.ScheduleEvent(EVENT_FLAME_BREATH, urand(10000, 15000)); - break; - case EVENT_LAVA_BURN: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + task.Repeat(Seconds(5), Seconds(13)); + }); + + scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + DoCastVictim(SPELL_FLAME_BREATH); + task.Repeat(Seconds(10), Seconds(15)); + }); + + scheduler.Schedule(Seconds(10), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f)) DoCast(target, SPELL_LAVA_BURN); - events.ScheduleEvent(EVENT_LAVA_BURN, urand(15000, 23000)); - break; - case EVENT_CAUTERIZING_FLAMES: - DoCast(SPELL_CAUTERIZING_FLAMES); - events.ScheduleEvent(EVENT_CAUTERIZING_FLAMES, urand(10000, 16000)); - break; - default: - break; - } + task.Repeat(Seconds(15), Seconds(23)); + }); - DoMeleeAttackIfReady(); - } + if (IsHeroic()) + { + scheduler.Schedule(Seconds(3), [this](TaskContext task) + { + DoCastAOE(SPELL_CAUTERIZING_FLAMES); + task.Repeat(Seconds(10), Seconds(16)); + }); + } + } + }; - void JustDied(Unit* /*killer*/) override + CreatureAI* GetAI(Creature* creature) const override { - if (instance->GetData(DATA_WAVE_COUNT) == 6) - { - instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 7); - } - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - { - instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 13); - } + return GetVioletHoldAI<boss_lavanthorAI>(creature); } - - private: - EventMap events; - InstanceScript* instance; - }; }; void AddSC_boss_lavanthor() diff --git a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp index ee89faac3a4..4c5c0373b8c 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp @@ -25,8 +25,10 @@ enum Spells { SPELL_CORROSIVE_SALIVA = 54527, SPELL_OPTIC_LINK = 54396, - SPELL_RAY_OF_PAIN = 54438, // NYI missing spelldifficulty - SPELL_RAY_OF_SUFFERING = 54442, // NYI missing spelldifficulty + SPELL_RAY_OF_PAIN = 54438, + SPELL_RAY_OF_PAIN_H = 59523, + SPELL_RAY_OF_SUFFERING = 54442, + SPELL_RAY_OF_SUFFERING_H = 59524, // Visual SPELL_OPTIC_LINK_LEVEL_1 = 54393, @@ -34,191 +36,107 @@ enum Spells SPELL_OPTIC_LINK_LEVEL_3 = 54395 }; -enum MoraggEvents -{ - EVENT_CORROSIVE_SALIVA = 1, - EVENT_OPTIC_LINK -}; - class boss_moragg : public CreatureScript { -public: - boss_moragg() : CreatureScript("boss_moragg") { } - - struct boss_moraggAI : public ScriptedAI - { - boss_moraggAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } - - void Reset() override - { - events.Reset(); - - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); - } + public: + boss_moragg() : CreatureScript("boss_moragg") { } - void EnterCombat(Unit* /*who*/) override + struct boss_moraggAI : public BossAI { - if (GameObject* door = instance->GetGameObject(DATA_MORAGG_CELL)) - if (door->GetGoState() == GO_STATE_READY) - { - EnterEvadeMode(); - return; - } - - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); - - me->SetInCombatWithZone(); - - DoCast(SPELL_RAY_OF_PAIN); - DoCast(SPELL_RAY_OF_SUFFERING); - events.ScheduleEvent(EVENT_OPTIC_LINK, 15000); - events.ScheduleEvent(EVENT_CORROSIVE_SALIVA, 5000); - } - - void AttackStart(Unit* who) override - { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; + boss_moraggAI(Creature* creature) : BossAI(creature, DATA_MORAGG) { } - if (me->Attack(who, true)) + void Reset() override { - me->AddThreat(who, 0.0f); - me->SetInCombatWith(who); - who->SetInCombatWith(me); - DoStartMovement(who); + BossAI::Reset(); } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) + void EnterCombat(Unit* who) override { - case EVENT_OPTIC_LINK: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_OPTIC_LINK); - events.ScheduleEvent(EVENT_OPTIC_LINK, 25000); - break; - case EVENT_CORROSIVE_SALIVA: - DoCastVictim(SPELL_CORROSIVE_SALIVA); - events.ScheduleEvent(EVENT_CORROSIVE_SALIVA, 10000); - break; - default: - break; + BossAI::EnterCombat(who); } - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - if (instance->GetData(DATA_WAVE_COUNT) == 6) + void JustReachedHome() override { - instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 7); + BossAI::JustReachedHome(); + instance->SetData(DATA_HANDLE_CELLS, DATA_MORAGG); } - else if (instance->GetData(DATA_WAVE_COUNT) == 12) + + void JustDied(Unit* killer) override { - instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 13); + BossAI::JustDied(killer); } - } - - private: - EventMap events; - InstanceScript* instance; - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_moraggAI>(creature); - } -}; - -class spell_moragg_ray_of_suffering : public SpellScriptLoader -{ -public: - spell_moragg_ray_of_suffering() : SpellScriptLoader("spell_moragg_ray_of_suffering") { } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - class spell_moragg_ray_of_suffering_AuraScript : public AuraScript - { - PrepareAuraScript(spell_moragg_ray_of_suffering_AuraScript); + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } - void OnPeriodic(AuraEffect const* aurEff) - { - PreventDefaultAction(); - std::list<HostileReference*> players = GetTarget()->getThreatManager().getThreatList(); - if (!players.empty()) + void ScheduleTasks() override { - std::list<HostileReference*>::iterator itr = players.begin(); - std::advance(itr, urand(0, players.size() - 1)); + scheduler.Async([this] + { + DoCast(me, DUNGEON_MODE(SPELL_RAY_OF_PAIN, SPELL_RAY_OF_PAIN_H)); + DoCast(me, DUNGEON_MODE(SPELL_RAY_OF_SUFFERING, SPELL_RAY_OF_SUFFERING_H)); + }); + + scheduler.Schedule(Seconds(15), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true)) + DoCast(target, SPELL_OPTIC_LINK); + task.Repeat(Seconds(25)); + }); - uint32 triggerSpell = GetSpellInfo()->GetEffect(aurEff->GetEffIndex())->TriggerSpell; - GetTarget()->CastCustomSpell(triggerSpell, SPELLVALUE_MAX_TARGETS, 1, (*itr)->getTarget(), TRIGGERED_FULL_MASK, NULL, aurEff); + scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + DoCastVictim(SPELL_CORROSIVE_SALIVA); + task.Repeat(Seconds(10)); + }); } - } + }; - void Register() override + CreatureAI* GetAI(Creature* creature) const override { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_ray_of_suffering_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + return GetVioletHoldAI<boss_moraggAI>(creature); } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_moragg_ray_of_suffering_AuraScript(); - } }; -class spell_moragg_ray_of_pain : public SpellScriptLoader +class spell_moragg_ray : public SpellScriptLoader { -public: - spell_moragg_ray_of_pain() : SpellScriptLoader("spell_moragg_ray_of_pain") { } + public: + spell_moragg_ray() : SpellScriptLoader("spell_moragg_ray") { } - class spell_moragg_ray_of_pain_AuraScript : public AuraScript - { - PrepareAuraScript(spell_moragg_ray_of_pain_AuraScript); - - void OnPeriodic(AuraEffect const* aurEff) + class spell_moragg_ray_AuraScript : public AuraScript { - PreventDefaultAction(); - std::list<HostileReference*> players = GetTarget()->getThreatManager().getThreatList(); - if (!players.empty()) + PrepareAuraScript(spell_moragg_ray_AuraScript); + + void OnPeriodic(AuraEffect const* aurEff) { - std::list<HostileReference*>::iterator itr = players.begin(); - std::advance(itr, urand(0, players.size() - 1)); + PreventDefaultAction(); + + if (!GetTarget()->IsAIEnabled) + return; - uint32 triggerSpell = GetSpellInfo()->GetEffect(aurEff->GetEffIndex())->TriggerSpell; - GetTarget()->CastCustomSpell(triggerSpell, SPELLVALUE_MAX_TARGETS, 1, (*itr)->getTarget(), TRIGGERED_FULL_MASK, NULL, aurEff); + if (Unit* target = GetTarget()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true)) + { + uint32 triggerSpell = aurEff->GetSpellEffectInfo()->TriggerSpell; + GetTarget()->CastSpell(target, triggerSpell, TRIGGERED_FULL_MASK, nullptr, aurEff); + } } - } - void Register() override + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_ray_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_ray_of_pain_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + return new spell_moragg_ray_AuraScript(); } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_moragg_ray_of_pain_AuraScript(); - } }; class spell_moragg_optic_link : public SpellScriptLoader @@ -232,30 +150,15 @@ public: void OnPeriodic(AuraEffect const* aurEff) { - switch (aurEff->GetTickNumber()) // Different visual based on tick + if (Unit* caster = GetCaster()) { - case 1: - case 2: - case 3: - GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_1, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); - break; - case 4: - case 5: - case 6: - case 7: - GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_1, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); - GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_2, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); - break; - case 8: - case 9: - case 10: - case 11: - GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_1, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); - GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_2, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); - GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_3, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff); - break; - default: - break; + if (aurEff->GetTickNumber() >= 8) + caster->CastSpell(GetTarget(), SPELL_OPTIC_LINK_LEVEL_3, TRIGGERED_FULL_MASK, nullptr, aurEff); + + if (aurEff->GetTickNumber() >= 4) + caster->CastSpell(GetTarget(), SPELL_OPTIC_LINK_LEVEL_2, TRIGGERED_FULL_MASK, nullptr, aurEff); + + caster->CastSpell(GetTarget(), SPELL_OPTIC_LINK_LEVEL_1, TRIGGERED_FULL_MASK, nullptr, aurEff); } } @@ -293,7 +196,6 @@ public: void AddSC_boss_moragg() { new boss_moragg(); - new spell_moragg_ray_of_suffering(); - new spell_moragg_ray_of_pain(); + new spell_moragg_ray(); new spell_moragg_optic_link(); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp index fe0f161cc27..62fcda47c9b 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp @@ -22,50 +22,53 @@ #include "Player.h" #include "violet_hold.h" +/* + * TODO: + * - Implement Ethereal Summon Target + */ + enum Spells { SPELL_ARCANE_BARRAGE_VOLLEY = 54202, SPELL_ARCANE_BUFFET = 54226, - SPELL_SUMMON_ETHEREAL_SPHERE_1 = 54102, - SPELL_SUMMON_ETHEREAL_SPHERE_2 = 61337, - SPELL_SUMMON_ETHEREAL_SPHERE_3 = 54138 + SPELL_SUMMON_TARGET_VISUAL = 54111 }; +static uint32 const EtherealSphereCount = 3; +static uint32 const EtherealSphereSummonSpells[EtherealSphereCount] = { 54102, 54137, 54138 }; +static uint32 const EtherealSphereHeroicSummonSpells[EtherealSphereCount] = { 54102, 54137, 54138 }; + enum NPCs { NPC_ETHEREAL_SPHERE = 29271, - NPC_ETHEREAL_SPHERE2 = 32582 + NPC_ETHEREAL_SPHERE2 = 32582, + NPC_ETHEREAL_SUMMON_TARGET = 29276 }; enum CreatureSpells { SPELL_ARCANE_POWER = 54160, H_SPELL_ARCANE_POWER = 59474, + SPELL_MAGIC_PULL = 50770, SPELL_SUMMON_PLAYERS = 54164, SPELL_POWER_BALL_VISUAL = 54141, - SPELL_POWER_BALL_DAMAGE_TRIGGER = 54207 + SPELL_POWER_BALL_DAMAGE_TRIGGER = 54207, + SPELL_POWER_BALL_DAMAGE_TRIGGER_H = 59476 }; enum Yells { + // Xevozz SAY_AGGRO = 0, SAY_SLAY = 1, SAY_DEATH = 2, SAY_SPAWN = 3, SAY_CHARGED = 4, SAY_REPEAT_SUMMON = 5, - SAY_SUMMON_ENERGY = 6 -}; + SAY_SUMMON_ENERGY = 6, -enum XevozzEvents -{ - EVENT_ARCANE_BARRAGE = 1, - EVENT_ARCANE_BUFFET, - EVENT_SUMMON_SPHERE, - EVENT_SUMMON_SPHERE_2, - EVENT_RANGE_CHECK, - EVENT_SUMMON_PLAYERS, - EVENT_DESPAWN_SPHERE + // Ethereal Sphere + SAY_ETHEREAL_SPHERE_SUMMON = 0 }; enum SphereActions @@ -75,319 +78,210 @@ enum SphereActions class boss_xevozz : public CreatureScript { -public: - boss_xevozz() : CreatureScript("boss_xevozz") { } - - struct boss_xevozzAI : public ScriptedAI - { - boss_xevozzAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } + public: + boss_xevozz() : CreatureScript("boss_xevozz") { } - void Reset() override + struct boss_xevozzAI : public BossAI { - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); + boss_xevozzAI(Creature* creature) : BossAI(creature, DATA_XEVOZZ) { } - DespawnSphere(); - events.Reset(); - } + void Reset() override + { + BossAI::Reset(); + } - void DespawnSphere() - { - std::list<Creature*> assistList; - GetCreatureListWithEntryInGrid(assistList, me, NPC_ETHEREAL_SPHERE, 150.0f); - GetCreatureListWithEntryInGrid(assistList, me, NPC_ETHEREAL_SPHERE2, 150.0f); + void EnterCombat(Unit* who) override + { + BossAI::EnterCombat(who); + Talk(SAY_AGGRO); + } - if (assistList.empty()) - return; + void JustReachedHome() override + { + BossAI::JustReachedHome(); + instance->SetData(DATA_HANDLE_CELLS, DATA_XEVOZZ); + } - for (std::list<Creature*>::const_iterator iter = assistList.begin(); iter != assistList.end(); ++iter) + void JustSummoned(Creature* summon) override { - if (Creature* pSphere = *iter) - pSphere->Kill(pSphere, false); + BossAI::JustSummoned(summon); + summon->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f); } - } - void JustSummoned(Creature* summoned) override - { - summoned->SetSpeed(MOVE_RUN, 0.5f); - summoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } - void AttackStart(Unit* who) override - { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } - if (me->Attack(who, true)) + void SpellHit(Unit* /*who*/, SpellInfo const* spell) override { - me->AddThreat(who, 0.0f); - me->SetInCombatWith(who); - who->SetInCombatWith(me); - DoStartMovement(who); + if (spell->Id == SPELL_ARCANE_POWER || spell->Id == H_SPELL_ARCANE_POWER) + Talk(SAY_SUMMON_ENERGY); } - } - void EnterCombat(Unit* /*who*/) override - { - if (GameObject* door = instance->GetGameObject(DATA_XEVOZZ_CELL)) - if (door->GetGoState() == GO_STATE_READY) - { - EnterEvadeMode(); + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) return; - } - - Talk(SAY_AGGRO); - - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); - - events.ScheduleEvent(EVENT_SUMMON_SPHERE, 5000); - events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(8000, 10000)); - events.ScheduleEvent(EVENT_ARCANE_BUFFET, urand(10000, 11000)); - } - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - - DespawnSphere(); - - if (instance->GetData(DATA_WAVE_COUNT) == 6) - { - instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 7); + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); } - else if (instance->GetData(DATA_WAVE_COUNT) == 12) + + void ScheduleTasks() override { - instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); - instance->SetData(DATA_WAVE_COUNT, 13); - } - } + scheduler.Schedule(Seconds(8), Seconds(10), [this](TaskContext task) + { + DoCastAOE(SPELL_ARCANE_BARRAGE_VOLLEY); + task.Repeat(Seconds(8), Seconds(10)); + }); - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } + scheduler.Schedule(Seconds(10), Seconds(11), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true)) + DoCast(target, SPELL_ARCANE_BUFFET); + task.Repeat(Seconds(15), Seconds(20)); + }); - void SpellHit(Unit* who, const SpellInfo* spell) override - { - if (!who->ToCreature()) - return; + scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + Talk(SAY_REPEAT_SUMMON); - if ((spell->Id == SPELL_ARCANE_POWER) || (spell->Id == H_SPELL_ARCANE_POWER)) - Talk(SAY_SUMMON_ENERGY); - } + std::list<uint8> summonSpells = { 0, 1, 2 }; - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + uint8 spell = Trinity::Containers::SelectRandomContainerElement(summonSpells); + DoCast(me, EtherealSphereSummonSpells[spell]); + summonSpells.remove(spell); - events.Update(diff); + if (IsHeroic()) + { + spell = Trinity::Containers::SelectRandomContainerElement(summonSpells); + task.Schedule(Milliseconds(2500), [this, spell](TaskContext /*task*/) + { + DoCast(me, EtherealSphereHeroicSummonSpells[spell]); + }); + } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + task.Schedule(Seconds(33), Seconds(35), [this](TaskContext /*task*/) + { + DummyEntryCheckPredicate pred; + summons.DoAction(ACTION_SUMMON, pred); + }); - switch (events.ExecuteEvent()) - { - case EVENT_ARCANE_BARRAGE: - DoCast(SPELL_ARCANE_BARRAGE_VOLLEY); - events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(8000, 10000)); - break; - case EVENT_ARCANE_BUFFET: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_ARCANE_BUFFET); - events.ScheduleEvent(EVENT_ARCANE_BUFFET, urand(15000, 20000)); - break; - case EVENT_SUMMON_SPHERE: - Talk(SAY_REPEAT_SUMMON); - DoCast(SPELL_SUMMON_ETHEREAL_SPHERE_1); - if (IsHeroic()) - events.ScheduleEvent(EVENT_SUMMON_SPHERE_2, 2500); - events.ScheduleEvent(EVENT_SUMMON_PLAYERS, urand(33000, 35000)); - events.ScheduleEvent(EVENT_SUMMON_SPHERE, urand(45000, 47000)); - break; - case EVENT_SUMMON_SPHERE_2: - Talk(SAY_REPEAT_SUMMON); - DoCast(SPELL_SUMMON_ETHEREAL_SPHERE_2); - break; - case EVENT_SUMMON_PLAYERS: - { - Creature* sphere = me->FindNearestCreature(NPC_ETHEREAL_SPHERE, 150.0f); - if (!sphere) - sphere = me->FindNearestCreature(NPC_ETHEREAL_SPHERE2, 150.0f); - if (sphere) - sphere->GetAI()->DoAction(ACTION_SUMMON); - break; - } - default: - break; + task.Repeat(Seconds(45), Seconds(47)); + }); } + }; - DoMeleeAttackIfReady(); + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<boss_xevozzAI>(creature); } - - private: - InstanceScript* instance; - EventMap events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_xevozzAI>(creature); - } }; class npc_ethereal_sphere : public CreatureScript { -public: - npc_ethereal_sphere() : CreatureScript("npc_ethereal_sphere") { } + public: + npc_ethereal_sphere() : CreatureScript("npc_ethereal_sphere") { } - struct npc_ethereal_sphereAI : public ScriptedAI - { - npc_ethereal_sphereAI(Creature* creature) : ScriptedAI(creature) + struct npc_ethereal_sphereAI : public ScriptedAI { - Initialize(); - instance = creature->GetInstanceScript(); - } + npc_ethereal_sphereAI(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + } - void Initialize() - { - arcanePower = false; - } + void Reset() override + { + scheduler.CancelAll(); + ScheduledTasks(); - void Reset() override - { - Initialize(); - events.Reset(); - DoCast(SPELL_POWER_BALL_VISUAL); - DoCast(SPELL_POWER_BALL_DAMAGE_TRIGGER); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->setFaction(16); - events.ScheduleEvent(EVENT_DESPAWN_SPHERE, 40000); - events.ScheduleEvent(EVENT_RANGE_CHECK, 1000); - } + DoCast(me, SPELL_POWER_BALL_VISUAL); + DoCast(me, DUNGEON_MODE(SPELL_POWER_BALL_DAMAGE_TRIGGER, SPELL_POWER_BALL_DAMAGE_TRIGGER_H)); - void DoAction(int32 action) override - { - if (action == ACTION_SUMMON) - DoCast(SPELL_SUMMON_PLAYERS); - } + me->DespawnOrUnsummon(40000); + } - void UpdateAI(uint32 diff) override - { - events.Update(diff); + void DoAction(int32 action) override + { + if (action == ACTION_SUMMON) + { + Talk(SAY_ETHEREAL_SPHERE_SUMMON); + DoCastAOE(SPELL_SUMMON_PLAYERS); + } + } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + void UpdateAI(uint32 diff) override + { + scheduler.Update(diff); + } - switch (events.ExecuteEvent()) + void ScheduledTasks() { - case EVENT_RANGE_CHECK: - if (Creature* xevozz = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_XEVOZZ))) + scheduler.Schedule(Seconds(1), [this](TaskContext task) + { + if (Creature* xevozz = instance->GetCreature(DATA_XEVOZZ)) { - if (me->IsWithinDist(xevozz, 3.0f) && !arcanePower) + if (me->IsWithinDist(xevozz, 3.0f)) { - DoCast(SPELL_ARCANE_POWER); - arcanePower = true; - events.ScheduleEvent(EVENT_DESPAWN_SPHERE, 8000); + DoCastAOE(SPELL_ARCANE_POWER); + me->DespawnOrUnsummon(8000); + return; } } - events.ScheduleEvent(EVENT_RANGE_CHECK, 1000); - break; - case EVENT_DESPAWN_SPHERE: - me->DespawnOrUnsummon(); - break; + task.Repeat(); + }); } - } - private: - InstanceScript* instance; - EventMap events; - bool arcanePower; - }; + private: + InstanceScript* instance; + TaskScheduler scheduler; + }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_ethereal_sphereAI>(creature); - } + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<npc_ethereal_sphereAI>(creature); + } }; class spell_xevozz_summon_players : public SpellScriptLoader { -public: - spell_xevozz_summon_players() : SpellScriptLoader("spell_xevozz_summon_players") { } - - class spell_xevozz_summon_players_SpellScript : public SpellScript - { - PrepareSpellScript(spell_xevozz_summon_players_SpellScript); + public: + spell_xevozz_summon_players() : SpellScriptLoader("spell_xevozz_summon_players") { } - void HandleScript(SpellEffIndex /*effIndex*/) + class spell_xevozz_summon_players_SpellScript : public SpellScript { - Unit* target = GetHitUnit(); + PrepareSpellScript(spell_xevozz_summon_players_SpellScript); - if (target) + bool Validate(SpellInfo const* /*spellInfo*/) override { - Position pos = GetOriginalCaster()->GetPosition(); - - target->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); + if (!sSpellMgr->GetSpellInfo(SPELL_MAGIC_PULL)) + return false; + return true; } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_xevozz_summon_players_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_xevozz_summon_players_SpellScript(); - } -}; - -class spell_xevozz_summon_ethereal_sphere : public SpellScriptLoader -{ -public: - spell_xevozz_summon_ethereal_sphere() : SpellScriptLoader("spell_xevozz_summon_ethereal_sphere") { } - - class spell_xevozz_summon_ethereal_sphere_SpellScript : public SpellScript - { - PrepareSpellScript(spell_xevozz_summon_ethereal_sphere_SpellScript); - void HandleScript(SpellDestination& target) - { - Unit* caster = GetOriginalCaster(); - Position pos; - float distance = 0.0f; - - while (distance < 20.0f) + void HandleScript(SpellEffIndex /*effIndex*/) { - pos = caster->GetRandomNearPosition(60.0f); - distance = caster->GetDistance(pos); + GetCaster()->CastSpell(GetHitUnit(), SPELL_MAGIC_PULL, true); } - target.Relocate(pos); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_xevozz_summon_players_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; - void Register() override + SpellScript* GetSpellScript() const override { - OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_xevozz_summon_ethereal_sphere_SpellScript::HandleScript, EFFECT_0, TARGET_DEST_DB); + return new spell_xevozz_summon_players_SpellScript(); } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_xevozz_summon_ethereal_sphere_SpellScript(); - } }; void AddSC_boss_xevozz() @@ -395,5 +289,4 @@ void AddSC_boss_xevozz() new boss_xevozz(); new npc_ethereal_sphere(); new spell_xevozz_summon_players(); - new spell_xevozz_summon_ethereal_sphere(); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp index 5b3f06c9e40..14d7b5fcd95 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp @@ -25,6 +25,10 @@ enum Spells SPELL_SUMMON_VOID_SENTRY = 54369, SPELL_VOID_SHIFT = 54361, SPELL_VOID_SHIFTED = 54343, + SPELL_ZURAMAT_ADD = 54341, + SPELL_ZURAMAT_ADD_2 = 54342, + SPELL_ZURAMAT_ADD_DUMMY = 54351, + SPELL_SUMMON_VOID_SENTRY_BALL = 58650 }; enum Yells @@ -39,188 +43,172 @@ enum Yells enum Misc { + ACTION_DESPAWN_VOID_SENTRY_BALL = 1, DATA_VOID_DANCE = 2153 }; -enum ZuramatEvents -{ - EVENT_VOID_SHIFT = 1, - EVENT_SUMMON_VOID, - EVENT_SHROUD_OF_DARKNESS -}; - class boss_zuramat : public CreatureScript { -public: - boss_zuramat() : CreatureScript("boss_zuramat") { } + public: + boss_zuramat() : CreatureScript("boss_zuramat") { } - struct boss_zuramatAI : public ScriptedAI - { - boss_zuramatAI(Creature* creature) : ScriptedAI(creature), sentries(me) + struct boss_zuramatAI : public BossAI { - Initialize(); - instance = creature->GetInstanceScript(); - } + boss_zuramatAI(Creature* creature) : BossAI(creature, DATA_ZURAMAT) + { + Initialize(); + } - void Initialize() - { - voidDance = true; - } + void Initialize() + { + _voidDance = true; + } - void DespawnSentries() - { - sentries.DespawnAll(); - std::list<Creature*> sentrylist; - GetCreatureListWithEntryInGrid(sentrylist, me, NPC_VOID_SENTRY_BALL, 200.0f); - if (!sentrylist.empty()) - for (std::list<Creature*>::const_iterator itr = sentrylist.begin(); itr != sentrylist.end(); ++itr) - (*itr)->DespawnOrUnsummon(); - } + void Reset() override + { + BossAI::Reset(); + Initialize(); + } - void Reset() override - { - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, NOT_STARTED); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED); - - Initialize(); - events.Reset(); - DespawnSentries(); - } + void EnterCombat(Unit* who) override + { + BossAI::EnterCombat(who); + Talk(SAY_AGGRO); + } - void AttackStart(Unit* who) override - { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; + void JustReachedHome() override + { + BossAI::JustReachedHome(); + instance->SetData(DATA_HANDLE_CELLS, DATA_ZURAMAT); + } - if (me->Attack(who, true)) + void SummonedCreatureDies(Creature* summon, Unit* /*who*/) override { - me->AddThreat(who, 0.0f); - me->SetInCombatWith(who); - who->SetInCombatWith(me); - DoStartMovement(who); + if (summon->GetEntry() == NPC_VOID_SENTRY) + _voidDance = false; } - } - void EnterCombat(Unit* /*who*/) override - { - if (GameObject* door = instance->GetGameObject(DATA_ZURAMAT_CELL)) - if (door->GetGoState() == GO_STATE_READY) - { - EnterEvadeMode(); + void SummonedCreatureDespawn(Creature* summon) override + { + if (summon->GetEntry() == NPC_VOID_SENTRY) + summon->AI()->DoAction(ACTION_DESPAWN_VOID_SENTRY_BALL); + BossAI::SummonedCreatureDespawn(summon); + } + + uint32 GetData(uint32 type) const override + { + if (type == DATA_VOID_DANCE) + return _voidDance ? 1 : 0; + + return 0; + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) return; - } - Talk(SAY_AGGRO); + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + void ScheduleTasks() override + { + scheduler.Schedule(Seconds(4), [this](TaskContext task) + { + DoCast(me, SPELL_SUMMON_VOID_SENTRY); + task.Repeat(Seconds(7), Seconds(10)); + }); - me->SetInCombatWithZone(); - events.ScheduleEvent(EVENT_SHROUD_OF_DARKNESS, urand(18000, 20000)); - events.ScheduleEvent(EVENT_VOID_SHIFT, 9000); - events.ScheduleEvent(EVENT_SUMMON_VOID, 4000); - } + scheduler.Schedule(Seconds(9), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, true)) + DoCast(target, SPELL_VOID_SHIFT); + task.Repeat(Seconds(15)); + }); - void JustSummoned(Creature* summon) override - { - sentries.Summon(summon); - } + scheduler.Schedule(Seconds(18), Seconds(20), [this](TaskContext task) + { + DoCast(me, SPELL_SHROUD_OF_DARKNESS); + task.Repeat(Seconds(18), Seconds(20)); + }); + } - void SummonedCreatureDies(Creature* summoned, Unit* /*who*/) override - { - if (summoned->GetEntry() == NPC_VOID_SENTRY) - voidDance = false; - } + private: + bool _voidDance; + }; - uint32 GetData(uint32 type) const override + CreatureAI* GetAI(Creature* creature) const override { - if (type == DATA_VOID_DANCE) - return voidDance ? 1 : 0; - - return 0; + return GetVioletHoldAI<boss_zuramatAI>(creature); } +}; - void JustDied(Unit* /*killer*/) override +class npc_void_sentry : public CreatureScript +{ + public: + npc_void_sentry() : CreatureScript("npc_void_sentry") { } + + struct npc_void_sentryAI : public ScriptedAI { - instance->SetData(DATA_ZURAMAT, 1); + npc_void_sentryAI(Creature* creature) : ScriptedAI(creature), _summons(creature) + { + me->SetReactState(REACT_PASSIVE); + } - Talk(SAY_DEATH); + void IsSummonedBy(Unit* /*summoner*/) override + { + me->CastSpell(me, SPELL_SUMMON_VOID_SENTRY_BALL, true); + } - DespawnSentries(); + void JustSummoned(Creature* summon) override + { + _summons.Summon(summon); + summon->SetReactState(REACT_PASSIVE); + } - if (instance->GetData(DATA_WAVE_COUNT) == 6) + void SummonedCreatureDespawn(Creature* summon) override { - instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 7); + _summons.Despawn(summon); } - else if (instance->GetData(DATA_WAVE_COUNT) == 12) + + void DoAction(int32 actionId) override { - instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 13); + if (actionId == ACTION_DESPAWN_VOID_SENTRY_BALL) + _summons.DespawnAll(); } - } - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } + void JustDied(Unit* /*killer*/) override + { + DoAction(ACTION_DESPAWN_VOID_SENTRY_BALL); + } - void UpdateAI(uint32 diff) override + private: + SummonList _summons; + }; + + CreatureAI* GetAI(Creature* creature) const override { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SUMMON_VOID: - DoCast(SPELL_SUMMON_VOID_SENTRY); - events.ScheduleEvent(EVENT_SUMMON_VOID, urand(7000, 10000)); - break; - case EVENT_VOID_SHIFT: - if (Unit* unit = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(unit, SPELL_VOID_SHIFT); - events.ScheduleEvent(EVENT_VOID_SHIFT, 15000); - break; - case EVENT_SHROUD_OF_DARKNESS: - DoCast(SPELL_SHROUD_OF_DARKNESS); - events.ScheduleEvent(EVENT_SHROUD_OF_DARKNESS, urand(18000, 20000)); - break; - default: - break; - } - - DoMeleeAttackIfReady(); + return GetVioletHoldAI<npc_void_sentryAI>(creature); } - - private: - InstanceScript* instance; - EventMap events; - SummonList sentries; - bool voidDance; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_zuramatAI>(creature); - } }; class achievement_void_dance : public AchievementCriteriaScript { public: - achievement_void_dance() : AchievementCriteriaScript("achievement_void_dance") - { - } + achievement_void_dance() : AchievementCriteriaScript("achievement_void_dance") { } bool OnCheck(Player* /*player*/, Unit* target) override { @@ -238,5 +226,6 @@ class achievement_void_dance : public AchievementCriteriaScript void AddSC_boss_zuramat() { new boss_zuramat(); + new npc_void_sentry(); new achievement_void_dance(); } diff --git a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp index e9c526df42e..9b51e5611ad 100644 --- a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp @@ -18,82 +18,150 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "InstanceScript.h" +#include "WorldStatePackets.h" #include "violet_hold.h" #include "Player.h" -#include "TemporarySummon.h" - -/* Violet Hold encounters: -0 - First boss -1 - Second boss -2 - Cyanigosa*/ - -/* Violet hold bosses: -1 - Moragg -2 - Erekem -3 - Ichoron -4 - Lavanthor -5 - Xevozz -6 - Zuramat -7 - Cyanigosa */ - -enum AzureSaboteurSpells + +/* + * TODO: + * - replace bosses by dummy npcs also after grid unload + */ + +Position const DefenseSystemLocation = { 1888.146f, 803.382f, 58.60389f, 3.071779f }; // sniff + +Position const CyanigosaSpawnLocation = { 1922.109f, 804.4493f, 52.49254f, 3.176499f }; // sniff +Position const CyanigosaJumpLocation = { 1888.32f, 804.473f, 38.3578f, 0.0f }; // sniff + +Position const SaboteurSpawnLocation = { 1886.251f, 803.0743f, 38.42326f, 3.211406f }; // sniff + +uint32 const PortalPositionsSize = 5; +Position const PortalPositions[PortalPositionsSize] = // sniff +{ + { 1877.523f, 850.1788f, 45.36822f, 4.34587f }, // 0 + { 1890.679f, 753.4202f, 48.771f, 1.675516f }, // 1 + { 1936.09f, 803.1875f, 54.09715f, 3.054326f }, // 2 + { 1858.243f, 770.2379f, 40.42146f, 0.9075712f }, // 3 + { 1907.288f, 831.1111f, 40.22015f, 3.560472f } // 4 +}; + +uint32 const PortalElitePositionsSize = 3; +Position const PortalElitePositions[PortalElitePositionsSize] = // sniff +{ + { 1911.281f, 800.9722f, 39.91673f, 3.01942f }, // 5 + { 1926.516f, 763.6616f, 52.35725f, 2.251475f }, // 6 + { 1922.464f, 847.0699f, 48.50161f, 3.961897f } // 7 +}; + +uint32 const PortalIntroPositionsSize = 5; +Position const PortalIntroPositions[PortalIntroPositionsSize] = // sniff +{ + { 1877.51f, 850.1042f, 44.65989f, 4.782202f }, // 0 - Intro + { 1890.637f, 753.4705f, 48.72239f, 1.710423f }, // 1 - Intro + { 1936.073f, 803.1979f, 53.37491f, 3.124139f }, // 2 - Intro + { 1886.545f, 803.2014f, 40.40931f, 3.159046f }, // 3 - Boss 1/2 + { 1924.096f, 804.3707f, 54.29256f, 3.228859f } // 4 - Boss 3 +}; + +uint32 const EncouterPortalsCount = PortalPositionsSize + PortalElitePositionsSize; + +uint32 const MoraggPathSize = 3; +G3D::Vector3 const MoraggPath[MoraggPathSize] = // sniff +{ + { 1893.895f, 728.1261f, 47.75016f }, + { 1892.997f, 738.4987f, 47.66684f }, + { 1889.76f, 758.1089f, 47.66684f } +}; + +uint32 const ErekemPathSize = 3; +G3D::Vector3 const ErekemPath[ErekemPathSize] = // sniff +{ + { 1871.456f, 871.0361f, 43.41524f }, + { 1874.948f, 859.5452f, 43.33349f }, + { 1877.245f, 851.967f, 43.3335f } +}; + +uint32 const ErekemGuardLeftPathSize = 3; +G3D::Vector3 const ErekemGuardLeftPath[ErekemGuardLeftPathSize] = // sniff +{ + { 1853.752f, 862.4528f, 43.41614f }, + { 1866.931f, 854.577f, 43.3335f }, + { 1872.973f, 850.7875f, 43.3335f } +}; + +uint32 const ErekemGuardRightPathSize = 3; +G3D::Vector3 const ErekemGuardRightPath[ErekemGuardRightPathSize] = // sniff +{ + { 1892.418f, 872.2831f, 43.41563f }, + { 1885.639f, 859.0245f, 43.3335f }, + { 1882.432f, 852.2423f, 43.3335f } +}; + +uint32 const IchoronPathSize = 5; +G3D::Vector3 const IchoronPath[IchoronPathSize] = // sniff { - SABOTEUR_SHIELD_DISRUPTION = 58291, - SABOTEUR_SHIELD_EFFECT = 45775 + { 1942.041f, 749.5228f, 30.95229f }, + { 1930.571f, 762.9065f, 31.98814f }, + { 1923.657f, 770.6718f, 34.07256f }, + { 1910.631f, 784.4096f, 37.09015f }, + { 1906.595f, 788.3828f, 37.99429f } }; -enum CrystalSpells +uint32 const LavanthorPathSize = 3; +G3D::Vector3 const LavanthorPath[LavanthorPathSize] = // sniff { - SPELL_ARCANE_LIGHTNING = 57930 + { 1844.557f, 748.7083f, 38.74205f }, + { 1854.618f, 761.5295f, 38.65631f }, + { 1862.17f, 773.2255f, 38.74879f } }; -Position const PortalLocation[] = +uint32 const XevozzPathSize = 3; +G3D::Vector3 const XevozzPath[XevozzPathSize] = // sniff { - {1877.51f, 850.104f, 44.6599f, 4.7822f }, // WP 1 - {1918.37f, 853.437f, 47.1624f, 4.12294f}, // WP 2 - {1936.07f, 803.198f, 53.3749f, 3.12414f}, // WP 3 - {1927.61f, 758.436f, 51.4533f, 2.20891f}, // WP 4 - {1890.64f, 753.471f, 48.7224f, 1.71042f}, // WP 5 - {1908.31f, 809.657f, 38.7037f, 3.08701f} // WP 6 + { 1908.417f, 845.8502f, 38.71947f }, + { 1905.557f, 841.3157f, 38.65529f }, + { 1899.453f, 832.533f, 38.70752f } +}; + +uint32 const ZuramatPathSize = 3; +G3D::Vector3 const ZuramatPath[ZuramatPathSize] = // sniff +{ + { 1934.151f, 860.9463f, 47.29499f }, + { 1927.085f, 852.1342f, 47.19214f }, + { 1923.226f, 847.3297f, 47.15541f } }; -Position const ArcaneSphere = {1887.060059f, 806.151001f, 61.321602f, 0.0f}; -Position const BossStartMove1 = {1894.684448f, 739.390503f, 47.668003f, 0.0f}; -Position const BossStartMove2 = {1875.173950f, 860.832703f, 43.333565f, 0.0f}; -Position const BossStartMove21 = {1858.854614f, 855.071411f, 43.333565f, 0.0f}; -Position const BossStartMove22 = {1891.926636f, 863.388977f, 43.333565f, 0.0f}; -Position const BossStartMove3 = {1916.138062f, 778.152222f, 35.772308f, 0.0f}; -Position const BossStartMove4 = {1853.618286f, 758.557617f, 38.657505f, 0.0f}; -Position const BossStartMove5 = {1906.683960f, 842.348022f, 38.637459f, 0.0f}; -Position const BossStartMove6 = {1928.207031f, 852.864441f, 47.200813f, 0.0f}; - -Position const CyanigosasSpawnLocation = {1930.281250f, 804.407715f, 52.410946f, 3.139621f}; -Position const MiddleRoomLocation = {1892.291260f, 805.696838f, 38.438862f, 3.139621f}; -Position const MiddleRoomPortalSaboLocation = {1896.622925f, 804.854126f, 38.504772f, 3.139621f}; - -// Cyanigosa's prefight event data enum Yells { - CYANIGOSA_SAY_SPAWN = 0 + SAY_CYANIGOSA_SPAWN = 3, + SAY_XEVOZZ_SPAWN = 3, + SAY_EREKEM_SPAWN = 3, + SAY_ICHORON_SPAWN = 3, + SAY_ZURAMAT_SPAWN = 3, + + SOUND_MORAGG_SPAWN = 10112 }; enum Spells { - CYANIGOSA_SPELL_TRANSFORM = 58668, - CYANIGOSA_BLUE_AURA = 47759, + SPELL_CYANIGOSA_TRANSFORM = 58668, + SPELL_CYANIGOSA_ARCANE_POWER_STATE = 49411, + SPELL_MORAGG_EMOTE_ROAR = 48350, + SPELL_LAVANTHOR_SPECIAL_UNARMED = 33334, + SPELL_ZURAMAT_COSMETIC_CHANNEL_OMNI = 57552 }; ObjectData const creatureData[] = { - { NPC_XEVOZZ, DATA_XEVOZZ }, - { NPC_LAVANTHOR, DATA_LAVANTHOR }, - { NPC_ICHORON, DATA_ICHORON }, - { NPC_ZURAMAT, DATA_ZURAMAT }, - { NPC_EREKEM, DATA_EREKEM }, - { NPC_MORAGG, DATA_MORAGG }, - { NPC_CYANIGOSA, DATA_CYANIGOSA }, - { NPC_SINCLARI, DATA_SINCLARI }, - { 0, 0 } // END + { NPC_XEVOZZ, DATA_XEVOZZ }, + { NPC_LAVANTHOR, DATA_LAVANTHOR }, + { NPC_ICHORON, DATA_ICHORON }, + { NPC_ZURAMAT, DATA_ZURAMAT }, + { NPC_EREKEM, DATA_EREKEM }, + { NPC_MORAGG, DATA_MORAGG }, + { NPC_CYANIGOSA, DATA_CYANIGOSA }, + { NPC_SINCLARI, DATA_SINCLARI }, + { NPC_SINCLARI_TRIGGER, DATA_SINCLARI_TRIGGER }, + { 0, 0 } // END }; ObjectData const gameObjectData[] = @@ -110,598 +178,770 @@ ObjectData const gameObjectData[] = { 0, 0 } // END }; +MinionData const minionData[] = +{ + { NPC_EREKEM_GUARD, DATA_EREKEM }, + { 0, 0, } // END +}; + class instance_violet_hold : public InstanceMapScript { -public: - instance_violet_hold() : InstanceMapScript("instance_violet_hold", 608) { } + public: + instance_violet_hold() : InstanceMapScript(VioletHoldScriptName, 608) { } - struct instance_violet_hold_InstanceMapScript : public InstanceScript - { - instance_violet_hold_InstanceMapScript(Map* map) : InstanceScript(map) + struct instance_violet_hold_InstanceMapScript : public InstanceScript { - SetHeaders(DataHeader); - SetBossNumber(EncounterCount); - LoadObjectData(creatureData, gameObjectData); - - uiRemoveNpc = 0; - - uiDoorIntegrity = 100; - - uiWaveCount = 0; - uiLocation = urand(0, 5); - uiFirstBoss = 0; - uiSecondBoss = 0; - uiCountErekemGuards = 0; - uiCountActivationCrystals = 0; - uiCyanigosaEventPhase = 1; - - uiActivationTimer = 5000; - uiDoorSpellTimer = 2000; - uiCyanigosaEventTimer = 3 * IN_MILLISECONDS; - - bActive = false; - bWiped = false; - bIsDoorSpellCast = false; - bCrystalActivated = false; - defenseless = true; - uiMainEventPhase = NOT_STARTED; - zuramatDead = false; - } + instance_violet_hold_InstanceMapScript(Map* map) : InstanceScript(map) + { + SetHeaders(DataHeader); + SetBossNumber(EncounterCount); + LoadObjectData(creatureData, gameObjectData); + LoadMinionData(minionData); - ObjectGuid uiErekemGuard[2]; + FirstBossId = 0; + SecondBossId = 0; - ObjectGuid uiTeleportationPortal; - ObjectGuid uiSaboteurPortal; + DoorIntegrity = 100; + WaveCount = 0; + EventState = NOT_STARTED; - ObjectGuid uiActivationCrystal[4]; + LastPortalLocation = urand(0, EncouterPortalsCount - 1); - uint32 uiActivationTimer; - uint32 uiCyanigosaEventTimer; - uint32 uiDoorSpellTimer; + Defenseless = true; + } - GuidSet trashMobs; // to kill with crystal + void OnCreatureCreate(Creature* creature) override + { + InstanceScript::OnCreatureCreate(creature); - uint8 uiWaveCount; - uint8 uiLocation; - uint8 uiFirstBoss; - uint8 uiSecondBoss; - uint8 uiRemoveNpc; + switch (creature->GetEntry()) + { + case NPC_EREKEM_GUARD: + for (uint8 i = 0; i < ErekemGuardCount; ++i) + if (ErekemGuardGUIDs[i].IsEmpty()) + { + ErekemGuardGUIDs[i] = creature->GetGUID(); + break; + } + break; + default: + break; + } + } - uint8 uiDoorIntegrity; + void OnCreatureRemove(Creature* creature) override + { + InstanceScript::OnCreatureRemove(creature); - uint8 uiCountErekemGuards; - uint8 uiCountActivationCrystals; - uint8 uiCyanigosaEventPhase; - uint8 uiMainEventPhase; // SPECIAL: pre event animations, IN_PROGRESS: event itself + switch (creature->GetEntry()) + { + case NPC_EREKEM_GUARD: + for (uint8 i = 0; i < ErekemGuardCount; ++i) + if (ErekemGuardGUIDs[i] == creature->GetGUID()) + { + ErekemGuardGUIDs[i].Clear(); + break; + } + break; + default: + break; + } + } - bool bActive; - bool bWiped; - bool bIsDoorSpellCast; - bool bCrystalActivated; - bool defenseless; - bool zuramatDead; + void OnGameObjectCreate(GameObject* go) override + { + InstanceScript::OnGameObjectCreate(go); - std::list<uint8> NpcAtDoorCastingList; + switch (go->GetEntry()) + { + case GO_ACTIVATION_CRYSTAL: + for (uint8 i = 0; i < ActivationCrystalCount; ++i) + if (ActivationCrystalGUIDs[i].IsEmpty()) + { + ActivationCrystalGUIDs[i] = go->GetGUID(); + break; + } + break; + default: + break; + } + } - void OnCreatureCreate(Creature* creature) override - { - InstanceScript::OnCreatureCreate(creature); + void OnGameObjectRemove(GameObject* go) override + { + InstanceScript::OnGameObjectRemove(go); + + switch (go->GetEntry()) + { + case GO_ACTIVATION_CRYSTAL: + for (uint8 i = 0; i < ActivationCrystalCount; ++i) + if (ActivationCrystalGUIDs[i] == go->GetGUID()) + { + ActivationCrystalGUIDs[i].Clear(); + break; + } + break; + default: + break; + } + } - switch (creature->GetEntry()) + void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& data) override { - case NPC_EREKEM_GUARD: - if (uiCountErekemGuards < 2) - { - uiErekemGuard[uiCountErekemGuards++] = creature->GetGUID(); - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); - } - break; - case NPC_CYANIGOSA: - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); - break; - default: - break; - case NPC_VOID_SENTRY: - if (zuramatDead) - { - creature->DespawnOrUnsummon(); - zuramatDead = false; - } - break; + data.Worldstates.emplace_back(uint32(WORLD_STATE_VH_SHOW), uint32(EventState == IN_PROGRESS ? 1 : 0)); + data.Worldstates.emplace_back(uint32(WORLD_STATE_VH_PRISON_STATE), uint32(DoorIntegrity)); + data.Worldstates.emplace_back(uint32(WORLD_STATE_VH_WAVE_COUNT), uint32(WaveCount)); } - /*if (creature->GetGUID() == uiFirstBoss || creature->GetGUID() == uiSecondBoss) + bool CheckRequiredBosses(uint32 bossId, Player const* player = nullptr) const override { - creature->AllLootRemovedFromCorpse(); - creature->RemoveLootMode(1); - }*/ - } + if (_SkipCheckRequiredBosses(player)) + return true; - void OnGameObjectCreate(GameObject* go) override - { - InstanceScript::OnGameObjectCreate(go); + switch (bossId) + { + case DATA_MORAGG: + case DATA_EREKEM: + case DATA_ICHORON: + case DATA_LAVANTHOR: + case DATA_XEVOZZ: + case DATA_ZURAMAT: + /// old code used cell door state to check this + if (!(WaveCount == 6 && FirstBossId == bossId) && !(WaveCount == 12 && SecondBossId == bossId)) + return false; + break; + case DATA_CYANIGOSA: + if (WaveCount < 18) + return false; + break; + default: + break; + } - switch (go->GetEntry()) + return true; + } + + bool SetBossState(uint32 type, EncounterState state) override { - case GO_ACTIVATION_CRYSTAL: - if (uiCountActivationCrystals < 4) - uiActivationCrystal[uiCountActivationCrystals++] = go->GetGUID(); - break; - default: - break; + if (!InstanceScript::SetBossState(type, state)) + return false; + + switch (type) + { + case DATA_1ST_BOSS: + if (state == DONE) + UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_EREKEM, nullptr); + break; + case DATA_2ND_BOSS: + if (state == DONE) + UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_MORAGG, nullptr); + break; + case DATA_CYANIGOSA: + if (state == DONE) + SetData(DATA_MAIN_EVENT_STATE, DONE); + break; + case DATA_MORAGG: + case DATA_EREKEM: + case DATA_ICHORON: + case DATA_LAVANTHOR: + case DATA_XEVOZZ: + case DATA_ZURAMAT: + // this won't work correctly because bossstate was initializd with TO_BE_DECIDED + if (WaveCount == 6) + SetBossState(DATA_1ST_BOSS, state); + else if (WaveCount == 12) + SetBossState(DATA_2ND_BOSS, state); + + if (state == DONE) + SetData(DATA_WAVE_COUNT, WaveCount + 1); + break; + default: + break; + } + + return true; } - } - bool SetBossState(uint32 type, EncounterState state) override - { - if (!InstanceScript::SetBossState(type, state)) - return false; + void SetData(uint32 type, uint32 data) override + { + switch (type) + { + case DATA_WAVE_COUNT: + WaveCount = data; + if (WaveCount) + { + Scheduler.Schedule(Seconds(IsBossWave(WaveCount - 1) ? 45 : 5), [this](TaskContext /*task*/) + { + AddWave(); + }); + } + break; + case DATA_DOOR_INTEGRITY: + DoorIntegrity = data; + Defenseless = false; + DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, DoorIntegrity); + break; + case DATA_START_BOSS_ENCOUNTER: + switch (WaveCount) + { + case 6: + StartBossEncounter(FirstBossId); + break; + case 12: + StartBossEncounter(SecondBossId); + break; + } + break; + case DATA_MAIN_EVENT_STATE: + EventState = data; + if (data == IN_PROGRESS) // Start event + { + DoUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, WaveCount); + DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, DoorIntegrity); + DoUpdateWorldState(WORLD_STATE_VH_SHOW, 1); - switch (type) + WaveCount = 1; + Scheduler.Async(std::bind(&instance_violet_hold_InstanceMapScript::AddWave, this)); + + for (uint8 i = 0; i < ActivationCrystalCount; ++i) + if (GameObject* crystal = instance->GetGameObject(ActivationCrystalGUIDs[i])) + crystal->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + } + else if (data == NOT_STARTED) + { + if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR)) + { + mainDoor->SetGoState(GO_STATE_ACTIVE); + mainDoor->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + } + + DoUpdateWorldState(WORLD_STATE_VH_SHOW, 0); + DoUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, WaveCount); + DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, DoorIntegrity); + + for (uint8 i = 0; i < ActivationCrystalCount; ++i) + if (GameObject* crystal = instance->GetGameObject(ActivationCrystalGUIDs[i])) + crystal->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + } + else if (data == DONE) + { + if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR)) + { + mainDoor->SetGoState(GO_STATE_ACTIVE); + mainDoor->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + } + + DoUpdateWorldState(WORLD_STATE_VH_SHOW, 0); + + for (uint8 i = 0; i < ActivationCrystalCount; ++i) + if (GameObject* crystal = instance->GetGameObject(ActivationCrystalGUIDs[i])) + crystal->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) + sinclari->AI()->DoAction(ACTION_SINCLARI_OUTRO); + } + break; + case DATA_HANDLE_CELLS: + HandleCells(data, false); + break; + } + } + + uint32 GetData(uint32 type) const override { - case DATA_1ST_BOSS_EVENT: - if (state == DONE) - UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_EREKEM, nullptr); - break; - case DATA_2ND_BOSS_EVENT: - if (state == DONE) - UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_MORAGG, nullptr); - break; - case DATA_CYANIGOSA: - if (state == DONE) - { - uiMainEventPhase = DONE; - if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR)) - mainDoor->SetGoState(GO_STATE_ACTIVE); - } - break; - default: - break; + switch (type) + { + case DATA_1ST_BOSS: + return FirstBossId; + case DATA_2ND_BOSS: + return SecondBossId; + case DATA_MAIN_EVENT_STATE: + return EventState; + case DATA_WAVE_COUNT: + return WaveCount; + case DATA_DOOR_INTEGRITY: + return DoorIntegrity; + case DATA_DEFENSELESS: + return Defenseless ? 1 : 0; + default: + break; + } + + return 0; } - return true; - } + ObjectGuid GetGuidData(uint32 type) const override + { + switch (type) + { + case DATA_EREKEM_GUARD_1: + case DATA_EREKEM_GUARD_2: + return ErekemGuardGUIDs[type - DATA_EREKEM_GUARD_1]; + default: + break; + } - void SetData(uint32 type, uint32 data) override - { - switch (type) + return InstanceScript::GetGuidData(type); + } + + void SpawnPortal() { - case DATA_WAVE_COUNT: - uiWaveCount = data; - bActive = true; - break; - case DATA_REMOVE_NPC: - uiRemoveNpc = data; - break; - case DATA_PORTAL_LOCATION: - uiLocation = (uint8)data; - break; - case DATA_DOOR_INTEGRITY: - uiDoorIntegrity = data; - defenseless = false; - DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, uiDoorIntegrity); - break; - case DATA_NPC_PRESENCE_AT_DOOR_ADD: - NpcAtDoorCastingList.push_back(data); - break; - case DATA_NPC_PRESENCE_AT_DOOR_REMOVE: - if (!NpcAtDoorCastingList.empty()) - NpcAtDoorCastingList.pop_back(); - break; - case DATA_MAIN_DOOR: - if (GameObject* mainDoor = GetGameObject(type)) - mainDoor->SetGoState(GOState(data)); - break; - case DATA_START_BOSS_ENCOUNTER: - switch (uiWaveCount) + LastPortalLocation = (LastPortalLocation + urand(1, EncouterPortalsCount - 1)) % (EncouterPortalsCount); + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) + { + if (LastPortalLocation < PortalPositionsSize) { - case 6: - StartBossEncounter(uiFirstBoss); - break; - case 12: - StartBossEncounter(uiSecondBoss); - break; + if (Creature* portal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalPositions[LastPortalLocation], TEMPSUMMON_CORPSE_DESPAWN)) + portal->AI()->SetData(DATA_PORTAL_LOCATION, LastPortalLocation); } - break; - case DATA_ACTIVATE_CRYSTAL: - ActivateCrystal(); - break; - case DATA_MAIN_EVENT_PHASE: - uiMainEventPhase = data; - if (data == IN_PROGRESS) // Start event + else { - if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR)) - mainDoor->SetGoState(GO_STATE_READY); - uiWaveCount = 1; - bActive = true; - for (int i = 0; i < 4; ++i) - if (GameObject* crystal = instance->GetGameObject(uiActivationCrystal[i])) - crystal->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - uiRemoveNpc = 0; // might not have been reset after a wipe on a boss. + if (Creature* portal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL_ELITE, PortalElitePositions[LastPortalLocation - PortalPositionsSize], TEMPSUMMON_CORPSE_DESPAWN)) + portal->AI()->SetData(DATA_PORTAL_LOCATION, LastPortalLocation); } - break; - case DATA_ZURAMAT: - zuramatDead = true; - break; + } } - } - void SetGuidData(uint32 type, ObjectGuid data) override - { - switch (type) + void HandleCells(uint8 bossId, bool open = true) { - case DATA_ADD_TRASH_MOB: - trashMobs.insert(data); - break; - case DATA_DEL_TRASH_MOB: - trashMobs.erase(data); - break; + switch (bossId) + { + case DATA_MORAGG: + HandleGameObject(GetObjectGuid(DATA_MORAGG_CELL), open); + break; + case DATA_EREKEM: + HandleGameObject(GetObjectGuid(DATA_EREKEM_CELL), open); + HandleGameObject(GetObjectGuid(DATA_EREKEM_LEFT_GUARD_CELL), open); + HandleGameObject(GetObjectGuid(DATA_EREKEM_RIGHT_GUARD_CELL), open); + break; + case DATA_ICHORON: + HandleGameObject(GetObjectGuid(DATA_ICHORON_CELL), open); + break; + case DATA_LAVANTHOR: + HandleGameObject(GetObjectGuid(DATA_LAVANTHOR_CELL), open); + break; + case DATA_XEVOZZ: + HandleGameObject(GetObjectGuid(DATA_XEVOZZ_CELL), open); + break; + case DATA_ZURAMAT: + HandleGameObject(GetObjectGuid(DATA_ZURAMAT_CELL), open); + break; + default: + break; + } } - } - uint32 GetData(uint32 type) const override - { - switch (type) + void StartBossEncounter(uint8 bossId) { - case DATA_WAVE_COUNT: return uiWaveCount; - case DATA_REMOVE_NPC: return uiRemoveNpc; - case DATA_PORTAL_LOCATION: return uiLocation; - case DATA_DOOR_INTEGRITY: return uiDoorIntegrity; - case DATA_NPC_PRESENCE_AT_DOOR: return NpcAtDoorCastingList.size(); - case DATA_FIRST_BOSS: return uiFirstBoss; - case DATA_SECOND_BOSS: return uiSecondBoss; - case DATA_MAIN_EVENT_PHASE: return uiMainEventPhase; - case DATA_DEFENSELESS: return defenseless ? 1 : 0; - } + switch (bossId) + { + case DATA_MORAGG: + Scheduler.Schedule(Seconds(2), [this](TaskContext task) + { + if (Creature* moragg = GetCreature(DATA_MORAGG)) + { + moragg->PlayDirectSound(SOUND_MORAGG_SPAWN); + moragg->CastSpell(moragg, SPELL_MORAGG_EMOTE_ROAR); + } + + task.Schedule(Seconds(3), [this](TaskContext task) + { + if (Creature* moragg = GetCreature(DATA_MORAGG)) + moragg->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, MoraggPath, MoraggPathSize, true); + + task.Schedule(Seconds(8), [this](TaskContext /*task*/) + { + if (Creature* moragg = GetCreature(DATA_MORAGG)) + { + moragg->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + moragg->AI()->DoZoneInCombat(moragg, 200.0f); + } + }); + }); + }); + break; + case DATA_EREKEM: + Scheduler.Schedule(Seconds(3), [this](TaskContext task) + { + if (Creature* erekem = GetCreature(DATA_EREKEM)) + erekem->AI()->Talk(SAY_EREKEM_SPAWN); + + task.Schedule(Seconds(5), [this](TaskContext task) + { + if (Creature* erekem = GetCreature(DATA_EREKEM)) + erekem->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, ErekemPath, ErekemPathSize, true); + + if (Creature* guard = instance->GetCreature(GetGuidData(DATA_EREKEM_GUARD_1))) + guard->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, ErekemGuardLeftPath, ErekemGuardLeftPathSize, true); + if (Creature* guard = instance->GetCreature(GetGuidData(DATA_EREKEM_GUARD_2))) + guard->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, ErekemGuardRightPath, ErekemGuardRightPathSize, true); + + task.Schedule(Seconds(6), [this](TaskContext task) + { + if (Creature* erekem = GetCreature(DATA_EREKEM)) + erekem->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + + task.Schedule(Seconds(1), [this](TaskContext /*task*/) + { + for (uint32 i = DATA_EREKEM_GUARD_1; i <= DATA_EREKEM_GUARD_2; ++i) + { + if (Creature* guard = instance->GetCreature(GetGuidData(i))) + guard->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + } + + if (Creature* erekem = GetCreature(DATA_EREKEM)) + { + erekem->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + erekem->AI()->DoZoneInCombat(erekem, 200.0f); + } + }); + }); + }); + }); + break; + case DATA_ICHORON: + Scheduler.Schedule(Seconds(2), [this](TaskContext task) + { + if (Creature* ichoron = GetCreature(DATA_ICHORON)) + ichoron->AI()->Talk(SAY_ICHORON_SPAWN); + + task.Schedule(Seconds(3), [this](TaskContext task) + { + if (Creature* ichoron = GetCreature(DATA_ICHORON)) + ichoron->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, IchoronPath, IchoronPathSize, true); + + task.Schedule(Seconds(14), [this](TaskContext /*task*/) + { + if (Creature* ichoron = GetCreature(DATA_ICHORON)) + { + ichoron->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + ichoron->AI()->DoZoneInCombat(ichoron, 200.0f); + } + }); + }); + }); + break; + case DATA_LAVANTHOR: + Scheduler.Schedule(Seconds(1), [this](TaskContext task) + { + if (Creature* lavanthor = GetCreature(DATA_LAVANTHOR)) + lavanthor->CastSpell(lavanthor, SPELL_LAVANTHOR_SPECIAL_UNARMED); + + task.Schedule(Seconds(3), [this](TaskContext task) + { + if (Creature* lavanthor = GetCreature(DATA_LAVANTHOR)) + lavanthor->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, LavanthorPath, LavanthorPathSize, true); + + task.Schedule(Seconds(8), [this](TaskContext /*task*/) + { + if (Creature* lavanthor = GetCreature(DATA_LAVANTHOR)) + { + lavanthor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + lavanthor->AI()->DoZoneInCombat(lavanthor, 200.0f); + } + }); + }); + }); + break; + case DATA_XEVOZZ: + Scheduler.Schedule(Seconds(2), [this](TaskContext task) + { + if (Creature* xevozz = GetCreature(DATA_XEVOZZ)) + xevozz->AI()->Talk(SAY_XEVOZZ_SPAWN); + + task.Schedule(Seconds(3), [this](TaskContext task) + { + if (Creature* xevozz = GetCreature(DATA_XEVOZZ)) + xevozz->HandleEmoteCommand(EMOTE_ONESHOT_TALK_NO_SHEATHE); + + task.Schedule(Seconds(4), [this](TaskContext task) + { + if (Creature* xevozz = GetCreature(DATA_XEVOZZ)) + xevozz->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, XevozzPath, XevozzPathSize, true); + + task.Schedule(Seconds(4), [this](TaskContext /*task*/) + { + if (Creature* xevozz = GetCreature(DATA_XEVOZZ)) + { + xevozz->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + xevozz->AI()->DoZoneInCombat(xevozz, 200.0f); + } + }); + }); + }); + }); + break; + case DATA_ZURAMAT: + Scheduler.Schedule(Seconds(2), [this](TaskContext task) + { + if (Creature* zuramat = GetCreature(DATA_ZURAMAT)) + { + zuramat->CastSpell(zuramat, SPELL_ZURAMAT_COSMETIC_CHANNEL_OMNI); + zuramat->AI()->Talk(SAY_ZURAMAT_SPAWN); + } + + task.Schedule(Seconds(6), [this](TaskContext task) + { + if (Creature* zuramat = GetCreature(DATA_ZURAMAT)) + zuramat->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, ZuramatPath, ZuramatPathSize, true); + + task.Schedule(Seconds(4), [this](TaskContext /*task*/) + { + if (Creature* zuramat = GetCreature(DATA_ZURAMAT)) + { + zuramat->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + zuramat->AI()->DoZoneInCombat(zuramat, 200.0f); + } + }); + }); + }); + break; + default: + return; + } - return 0; - } + HandleCells(bossId); + } - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) + void ResetBossEncounter(uint8 bossId) { - case DATA_EREKEM_GUARD_1: return uiErekemGuard[0]; - case DATA_EREKEM_GUARD_2: return uiErekemGuard[1]; - case DATA_TELEPORTATION_PORTAL: return uiTeleportationPortal; - case DATA_SABOTEUR_PORTAL: return uiSaboteurPortal; - } + if (bossId < DATA_CYANIGOSA || bossId > DATA_ZURAMAT) + return; - return InstanceScript::GetGuidData(type); - } + Creature* boss = GetCreature(bossId); + if (!boss) + return; - void SpawnPortal() - { - SetData(DATA_PORTAL_LOCATION, (GetData(DATA_PORTAL_LOCATION) + urand(1, 5))%6); - if (Creature* sinclari = GetCreature(DATA_SINCLARI)) - if (Creature* portal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocation[GetData(DATA_PORTAL_LOCATION)], TEMPSUMMON_CORPSE_DESPAWN)) - uiTeleportationPortal = portal->GetGUID(); - } + switch (bossId) + { + case DATA_CYANIGOSA: + boss->DespawnOrUnsummon(); + break; + case DATA_EREKEM: + for (uint32 i = DATA_EREKEM_GUARD_1; i <= DATA_EREKEM_GUARD_2; ++i) + { + if (Creature* guard = instance->GetCreature(GetGuidData(i))) + { + if (guard->isDead()) + guard->Respawn(); - void StartBossEncounter(uint8 uiBoss, bool bForceRespawn = true) - { - Creature* boss = nullptr; + if (GetBossState(bossId) == DONE) + UpdateKilledBoss(guard); - switch (uiBoss) - { - case BOSS_MORAGG: - HandleGameObject(GetObjectGuid(DATA_MORAGG_CELL), bForceRespawn); - boss = GetCreature(DATA_MORAGG); - if (boss) - boss->GetMotionMaster()->MovePoint(0, BossStartMove1); - break; - case BOSS_EREKEM: - HandleGameObject(GetObjectGuid(DATA_EREKEM_CELL), bForceRespawn); - HandleGameObject(GetObjectGuid(DATA_EREKEM_LEFT_GUARD_CELL), bForceRespawn); - HandleGameObject(GetObjectGuid(DATA_EREKEM_RIGHT_GUARD_CELL), bForceRespawn); - - boss = GetCreature(DATA_EREKEM); - if (boss) - boss->GetMotionMaster()->MovePoint(0, BossStartMove2); - - if (Creature* pGuard1 = instance->GetCreature(uiErekemGuard[0])) - { - if (bForceRespawn) + guard->GetMotionMaster()->MoveTargetedHome(); + guard->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + } + } + // no break + default: + if (boss->isDead()) { - pGuard1->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); - pGuard1->GetMotionMaster()->MovePoint(0, BossStartMove21); + // respawn and update to a placeholder npc to avoid be looted again + boss->Respawn(); + UpdateKilledBoss(boss); } - else - pGuard1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - } - if (Creature* pGuard2 = instance->GetCreature(uiErekemGuard[1])) - { - if (bForceRespawn) + boss->GetMotionMaster()->MoveTargetedHome(); + boss->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + break; + } + } + + void AddWave() + { + DoUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, WaveCount); + + switch (WaveCount) + { + case 6: + if (FirstBossId == 0) + FirstBossId = urand(DATA_MORAGG, DATA_ZURAMAT); + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) { - pGuard2->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - pGuard2->GetMotionMaster()->MovePoint(0, BossStartMove22); + sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL_INTRO, PortalIntroPositions[3], TEMPSUMMON_TIMED_DESPAWN, 3000); + sinclari->SummonCreature(NPC_SABOTEOUR, SaboteurSpawnLocation, TEMPSUMMON_DEAD_DESPAWN); } - else - pGuard2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - } - break; - case BOSS_ICHORON: - HandleGameObject(GetObjectGuid(DATA_ICHORON_CELL), bForceRespawn); - boss = GetCreature(DATA_ICHORON); - if (boss) - boss->GetMotionMaster()->MovePoint(0, BossStartMove3); - break; - case BOSS_LAVANTHOR: - HandleGameObject(GetObjectGuid(DATA_LAVANTHOR_CELL), bForceRespawn); - boss = GetCreature(DATA_LAVANTHOR); - if (boss) - boss->GetMotionMaster()->MovePoint(0, BossStartMove4); - break; - case BOSS_XEVOZZ: - HandleGameObject(GetObjectGuid(DATA_XEVOZZ_CELL), bForceRespawn); - boss = GetCreature(DATA_XEVOZZ); - if (boss) - boss->GetMotionMaster()->MovePoint(0, BossStartMove5); - break; - case BOSS_ZURAMAT: - HandleGameObject(GetObjectGuid(DATA_ZURAMAT_CELL), bForceRespawn); - boss = GetCreature(DATA_ZURAMAT); - if (boss) - boss->GetMotionMaster()->MovePoint(0, BossStartMove6); - break; + break; + case 12: + if (SecondBossId == 0) + do + { + SecondBossId = urand(DATA_MORAGG, DATA_ZURAMAT); + } while (SecondBossId == FirstBossId); + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) + { + sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL_INTRO, PortalIntroPositions[3], TEMPSUMMON_TIMED_DESPAWN, 3000); + sinclari->SummonCreature(NPC_SABOTEOUR, SaboteurSpawnLocation, TEMPSUMMON_DEAD_DESPAWN); + } + break; + case 18: + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) + { + sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL_INTRO, PortalIntroPositions[4], TEMPSUMMON_TIMED_DESPAWN, 6000); + if (Creature* cyanigosa = sinclari->SummonCreature(NPC_CYANIGOSA, CyanigosaSpawnLocation, TEMPSUMMON_DEAD_DESPAWN)) + cyanigosa->CastSpell(cyanigosa, SPELL_CYANIGOSA_ARCANE_POWER_STATE, true); + ScheduleCyanigosaIntro(); + } + break; + default: + SpawnPortal(); + break; + } } - // generic boss state changes - if (boss) + void WriteSaveDataMore(std::ostringstream& data) override { - boss->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - boss->SetReactState(REACT_AGGRESSIVE); + data << FirstBossId << ' ' << SecondBossId; + } + + void ReadSaveDataMore(std::istringstream& data) override + { + data >> FirstBossId; + data >> SecondBossId; + } - if (!bForceRespawn) + bool CheckWipe() const + { + Map::PlayerList const& players = instance->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) { - if (boss->isDead()) - { - // respawn but avoid to be looted again - boss->Respawn(); - boss->RemoveLootMode(1); - } - else - boss->GetMotionMaster()->MoveTargetedHome(); + Player* player = itr->GetSource(); + if (player->IsGameMaster()) + continue; - boss->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - uiWaveCount = 0; + if (player->IsAlive()) + return false; } - } - } - void AddWave() - { - DoUpdateWorldState(WORLD_STATE_VH, 1); - DoUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, uiWaveCount); + return true; + } - switch (uiWaveCount) + void UpdateKilledBoss(Creature* boss) { - case 6: - if (uiFirstBoss == 0) - uiFirstBoss = urand(1, 6); - if (Creature* sinclari = GetCreature(DATA_SINCLARI)) - { - if (Creature* portal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN)) - uiSaboteurPortal = portal->GetGUID(); - if (Creature* azureSaboteur = sinclari->SummonCreature(NPC_SABOTEOUR, MiddleRoomLocation, TEMPSUMMON_DEAD_DESPAWN)) - azureSaboteur->CastSpell(azureSaboteur, SABOTEUR_SHIELD_EFFECT, false); - } - break; - case 12: - if (uiSecondBoss == 0) - do - { - uiSecondBoss = urand(1, 6); - } while (uiSecondBoss == uiFirstBoss); - if (Creature* sinclari = GetCreature(DATA_SINCLARI)) - { - if (Creature* pPortal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN)) - uiSaboteurPortal = pPortal->GetGUID(); - if (Creature* pAzureSaboteur = sinclari->SummonCreature(NPC_SABOTEOUR, MiddleRoomLocation, TEMPSUMMON_DEAD_DESPAWN)) - pAzureSaboteur->CastSpell(pAzureSaboteur, SABOTEUR_SHIELD_EFFECT, false); - } - break; - case 18: - if (Creature* sinclari = GetCreature(DATA_SINCLARI)) - sinclari->SummonCreature(NPC_CYANIGOSA, CyanigosasSpawnLocation, TEMPSUMMON_DEAD_DESPAWN); - break; - case 1: + switch (boss->GetEntry()) { - if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR)) - mainDoor->SetGoState(GO_STATE_READY); - DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, 100); - // no break + case NPC_XEVOZZ: + boss->UpdateEntry(NPC_DUMMY_XEVOZZ); + break; + case NPC_LAVANTHOR: + boss->UpdateEntry(NPC_DUMMY_LAVANTHOR); + break; + case NPC_ICHORON: + boss->UpdateEntry(NPC_DUMMY_ICHORON); + break; + case NPC_ZURAMAT: + boss->UpdateEntry(NPC_DUMMY_ZURAMAT); + break; + case NPC_EREKEM: + boss->UpdateEntry(NPC_DUMMY_EREKEM); + break; + case NPC_MORAGG: + boss->UpdateEntry(NPC_DUMMY_MORAGG); + break; + case NPC_EREKEM_GUARD: + boss->UpdateEntry(NPC_DUMMY_EREKEM_GUARD); + break; + default: + break; } - default: - SpawnPortal(); - break; } - } - void WriteSaveDataMore(std::ostringstream& data) override - { - data << uiFirstBoss << ' ' << uiSecondBoss; - } + void Update(uint32 diff) override + { + if (!instance->HavePlayers()) + return; - void ReadSaveDataMore(std::istringstream& data) override - { - data >> uiFirstBoss; - data >> uiSecondBoss; - } + // if main event is in progress and players have wiped then reset instance + if ((EventState == IN_PROGRESS && CheckWipe()) || EventState == FAIL) + { + ResetBossEncounter(FirstBossId); + ResetBossEncounter(SecondBossId); + ResetBossEncounter(DATA_CYANIGOSA); - bool CheckWipe() - { - Map::PlayerList const &players = instance->GetPlayers(); - for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) - { - Player* player = itr->GetSource(); - if (player->IsGameMaster()) - continue; + WaveCount = 0; + DoorIntegrity = 100; + Defenseless = true; + SetData(DATA_MAIN_EVENT_STATE, NOT_STARTED); - if (player->IsAlive()) - return false; - } + Scheduler.CancelAll(); - zuramatDead = false; - return true; - } + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) + sinclari->AI()->EnterEvadeMode(); + } - void Update(uint32 diff) override - { - if (!instance->HavePlayers()) - return; + Scheduler.Update(diff); - // portals should spawn if other portal is dead and doors are closed - if (bActive && uiMainEventPhase == IN_PROGRESS) - { - if (uiActivationTimer < diff) + if (EventState == IN_PROGRESS) { - AddWave(); - bActive = false; - // 1 minute waiting time after each boss fight - uiActivationTimer = (uiWaveCount == 6 || uiWaveCount == 12) ? 60000 : 5000; - } else uiActivationTimer -= diff; + // if door is destroyed, event is failed + if (!GetData(DATA_DOOR_INTEGRITY)) + EventState = FAIL; + } } - // if main event is in progress and players have wiped then reset instance - if (uiMainEventPhase == IN_PROGRESS && CheckWipe()) + void ScheduleCyanigosaIntro() { - SetData(DATA_REMOVE_NPC, 1); - StartBossEncounter(uiFirstBoss, false); - StartBossEncounter(uiSecondBoss, false); - - SetData(DATA_MAIN_DOOR, GO_STATE_ACTIVE); - SetData(DATA_WAVE_COUNT, 0); - uiMainEventPhase = NOT_STARTED; - uiActivationTimer = 5000; - - for (int i = 0; i < 4; ++i) - if (GameObject* crystal = instance->GetGameObject(uiActivationCrystal[i])) - crystal->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - - if (Creature* sinclari = GetCreature(DATA_SINCLARI)) + Scheduler.Schedule(Seconds(2), [this](TaskContext task) { - sinclari->SetVisible(true); + if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA)) + cyanigosa->AI()->Talk(SAY_CYANIGOSA_SPAWN); - std::list<Creature*> GuardList; - sinclari->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); - if (!GuardList.empty()) + task.Schedule(Seconds(6), [this](TaskContext task) { - for (Creature* guard : GuardList) + if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA)) + cyanigosa->GetMotionMaster()->MoveJump(CyanigosaJumpLocation, 10.0f, 27.44744f); + + task.Schedule(Seconds(7), [this](TaskContext /*task*/) { - guard->SetVisible(true); - guard->SetReactState(REACT_AGGRESSIVE); - guard->GetMotionMaster()->MovePoint(1, guard->GetHomePosition()); - } - } - sinclari->GetMotionMaster()->MovePoint(1, sinclari->GetHomePosition()); - sinclari->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - } + if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA)) + { + cyanigosa->RemoveAurasDueToSpell(SPELL_CYANIGOSA_ARCANE_POWER_STATE); + cyanigosa->CastSpell(cyanigosa, SPELL_CYANIGOSA_TRANSFORM, true); + cyanigosa->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + } + }); + }); + }); } - // Cyanigosa is spawned but not tranformed, prefight event - Creature* cyanigosa = GetCreature(DATA_CYANIGOSA); - if (cyanigosa && !cyanigosa->HasAura(CYANIGOSA_SPELL_TRANSFORM)) + void ProcessEvent(WorldObject* /*go*/, uint32 eventId) override { - if (uiCyanigosaEventTimer <= diff) + if (eventId == EVENT_ACTIVATE_CRYSTAL) { - switch (uiCyanigosaEventPhase) - { - case 1: - cyanigosa->CastSpell(cyanigosa, CYANIGOSA_BLUE_AURA, false); - cyanigosa->AI()->Talk(CYANIGOSA_SAY_SPAWN); - uiCyanigosaEventTimer = 7*IN_MILLISECONDS; - ++uiCyanigosaEventPhase; - break; - case 2: - cyanigosa->GetMotionMaster()->MoveJump(MiddleRoomLocation.GetPositionX(), MiddleRoomLocation.GetPositionY(), MiddleRoomLocation.GetPositionZ(), 10.0f, 20.0f); - cyanigosa->CastSpell(cyanigosa, CYANIGOSA_BLUE_AURA, false); - uiCyanigosaEventTimer = 7*IN_MILLISECONDS; - ++uiCyanigosaEventPhase; - break; - case 3: - cyanigosa->RemoveAurasDueToSpell(CYANIGOSA_BLUE_AURA); - cyanigosa->CastSpell(cyanigosa, CYANIGOSA_SPELL_TRANSFORM, 0); - cyanigosa->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - cyanigosa->SetReactState(REACT_AGGRESSIVE); - uiCyanigosaEventTimer = 2*IN_MILLISECONDS; - ++uiCyanigosaEventPhase; - break; - case 4: - uiCyanigosaEventPhase = 0; - break; - } - } else uiCyanigosaEventTimer -= diff; + instance->SummonCreature(NPC_DEFENSE_SYSTEM, DefenseSystemLocation); + Defenseless = false; + } } - // if there are NPCs in front of the prison door, which are casting the door seal spell and doors are active - if (GetData(DATA_NPC_PRESENCE_AT_DOOR) && uiMainEventPhase == IN_PROGRESS) + static bool IsBossWave(uint8 wave) { - // if door integrity is > 0 then decrase it's integrity state - if (GetData(DATA_DOOR_INTEGRITY)) - { - if (uiDoorSpellTimer < diff) - { - SetData(DATA_DOOR_INTEGRITY, GetData(DATA_DOOR_INTEGRITY)-1); - uiDoorSpellTimer =2000; - } else uiDoorSpellTimer -= diff; - } - // else set door state to active (means door will open and group have failed to sustain mob invasion on the door) - else - { - SetData(DATA_MAIN_DOOR, GO_STATE_ACTIVE); - uiMainEventPhase = FAIL; - } + return wave && ((wave % 6) == 0); } - } - void ActivateCrystal() - { - // just to make things easier we'll get the gameobject from the map - GameObject* invoker = instance->GetGameObject(uiActivationCrystal[0]); - if (!invoker) - return; + protected: + TaskScheduler Scheduler; - SpellInfo const* spellInfoLightning = sSpellMgr->GetSpellInfo(SPELL_ARCANE_LIGHTNING); - if (!spellInfoLightning) - return; + static uint8 const ErekemGuardCount = 2; + ObjectGuid ErekemGuardGUIDs[ErekemGuardCount]; - // the orb - TempSummon* trigger = invoker->SummonCreature(NPC_DEFENSE_SYSTEM, ArcaneSphere, TEMPSUMMON_MANUAL_DESPAWN, 0); - if (!trigger) - return; + static uint8 const ActivationCrystalCount = 5; + ObjectGuid ActivationCrystalGUIDs[ActivationCrystalCount]; - // visuals - trigger->CastSpell(trigger, spellInfoLightning, true, 0, 0, trigger->GetGUID()); + uint32 FirstBossId; + uint32 SecondBossId; - // Kill all mobs registered with SetGuidData(ADD_TRASH_MOB) - for (GuidSet::const_iterator itr = trashMobs.begin(); itr != trashMobs.end();) - { - Creature* creature = instance->GetCreature(*itr); - // Increment the iterator before killing the creature because the kill will remove itr from trashMobs - ++itr; - if (creature && creature->IsAlive()) - trigger->Kill(creature); - } - } + uint8 DoorIntegrity; + uint8 WaveCount; + uint8 EventState; + uint8 LastPortalLocation; + + bool Defenseless; + }; - void ProcessEvent(WorldObject* /*go*/, uint32 uiEventId) override + InstanceScript* GetInstanceScript(InstanceMap* map) const override { - switch (uiEventId) - { - case EVENT_ACTIVATE_CRYSTAL: - bCrystalActivated = true; // Activation by player's will throw event signal - ActivateCrystal(); - break; - } + return new instance_violet_hold_InstanceMapScript(map); } - }; - - InstanceScript* GetInstanceScript(InstanceMap* map) const override - { - return new instance_violet_hold_InstanceMapScript(map); - } }; void AddSC_instance_violet_hold() diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp index b05da4b994c..fdb4c4dc3fc 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp @@ -15,36 +15,41 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "Player.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "ScriptedGossip.h" #include "ScriptedEscortAI.h" -#include "violet_hold.h" -#include "Player.h" -#include "SpellAuras.h" #include "SpellAuraEffects.h" #include "SpellScript.h" +#include "violet_hold.h" -#define GOSSIP_START_EVENT "Get your people to safety, we'll keep the Blue Dragonflight's forces at bay." -#define GOSSIP_ITEM_1 "Activate the crystals when we get in trouble, right" -#define GOSSIP_I_WANT_IN "I'm not fighting, so send me in now!" -#define SAY_EVENT_LOCK "I'm locking the door. Good luck, and thank you for doing this." -#define SPAWN_TIME 20000 +/* + * TODO: + * - add missing trash emotes + */ -enum PortalCreatures +enum PortalCreatureIds { NPC_AZURE_INVADER_1 = 30661, - NPC_AZURE_INVADER_2 = 30961, NPC_AZURE_SPELLBREAKER_1 = 30662, - NPC_AZURE_SPELLBREAKER_2 = 30962, NPC_AZURE_BINDER_1 = 30663, - NPC_AZURE_BINDER_2 = 30918, NPC_AZURE_MAGE_SLAYER_1 = 30664, + NPC_VETERAN_MAGE_HUNTER = 30665, + NPC_AZURE_CAPTAIN_1 = 30666, + NPC_AZURE_SORCEROR_1 = 30667, + NPC_AZURE_RAIDER_1 = 30668, + + NPC_AZURE_BINDER_2 = 30918, + NPC_AZURE_INVADER_2 = 30961, + NPC_AZURE_SPELLBREAKER_2 = 30962, NPC_AZURE_MAGE_SLAYER_2 = 30963, - NPC_AZURE_CAPTAIN = 30666, - NPC_AZURE_SORCEROR = 30667, - NPC_AZURE_RAIDER = 30668, - NPC_AZURE_STALKER = 32191 + NPC_AZURE_BINDER_3 = 31007, + NPC_AZURE_INVADER_3 = 31008, + NPC_AZURE_SPELLBREAKER_3 = 31009, + NPC_AZURE_MAGE_SLAYER_3 = 31010, + NPC_AZURE_RAIDER_2 = 31118, + NPC_AZURE_STALKER_1 = 32191 }; enum AzureInvaderSpells @@ -103,8 +108,8 @@ enum AzureStalkerSpells enum AzureSaboteurSpells { - SABOTEUR_SHIELD_DISRUPTION = 58291, - SABOTEUR_SHIELD_EFFECT = 45775 + SPELL_SHIELD_DISRUPTION = 58291, + SPELL_TELEPORT_VISUAL = 51347 }; enum TrashDoorSpell @@ -112,19 +117,45 @@ enum TrashDoorSpell SPELL_DESTROY_DOOR_SEAL = 58040 }; -enum Spells +enum DefenseSystemSpells +{ + SPELL_ARCANE_LIGHTNING_DAMAGE = 57912, + SPELL_ARCANE_LIGHTNING_INSTAKILL = 58152, + SPELL_ARCANE_LIGHTNING_DUMMY = 57930 +}; + +enum MiscSpells +{ + SPELL_PORTAL_PERIODIC = 58008, + SPELL_PORTAL_CHANNEL = 58012, + SPELL_CRYSTAL_ACTIVATION = 57804, + + SPELL_TELEPORT_PLAYER = 62138, + SPELL_TELEPORT_PLAYER_EFFECT = 62139 +}; + +enum MiscData { - SPELL_PORTAL_CHANNEL = 58012, - SPELL_CRYSTAL_ACTIVATION = 57804, // visual effect - SPELL_ARCANE_SPHERE_PASSIVE = 44263 + DATA_PORTAL_PERIODIC_TICK = 1 }; enum Sinclari { - SAY_SINCLARI_1 = 0 + // Sinclari + SAY_SINCLARI_INTRO_1 = 0, + SAY_SINCLARI_INTRO_2 = 1, + SAY_SINCLARI_OUTRO = 2, + + GOSSIP_MENU_START_ENCOUNTER = 9998, + GOSSIP_MENU_SEND_ME_IN = 10275, + + // Sinclari Trigger + SAY_SINCLARI_ELITE_SQUAD = 0, + SAY_SINCLARI_PORTAL_GUARDIAN = 1, + SAY_SINCLARI_PORTAL_KEEPER = 2 }; -float FirstPortalWPs [6][3] = +G3D::Vector3 const FirstPortalWPs[6] = { {1877.670288f, 842.280273f, 43.333591f}, {1877.338867f, 834.615356f, 38.762287f}, @@ -135,7 +166,7 @@ float FirstPortalWPs [6][3] = //{1825.736084f, 807.305847f, 44.363785f} }; -float SecondPortalFirstWPs [9][3] = +G3D::Vector3 const SecondPortalFirstWPs[9] = { {1902.561401f, 853.334656f, 47.106117f}, {1895.486084f, 855.376404f, 44.334591f}, @@ -149,7 +180,7 @@ float SecondPortalFirstWPs [9][3] = //{1825.736084f, 807.305847f, 44.363785f} }; -float SecondPortalSecondWPs [8][3] = +G3D::Vector3 const SecondPortalSecondWPs[8] = { {1929.392212f, 837.614990f, 47.136166f}, {1928.290649f, 824.750427f, 45.474411f}, @@ -162,7 +193,7 @@ float SecondPortalSecondWPs [8][3] = //{1825.736084f, 807.305847f, 44.363785f} }; -float ThirdPortalWPs [8][3] = +G3D::Vector3 const ThirdPortalWPs[8] = { {1934.049438f, 815.778503f, 52.408699f}, {1928.290649f, 824.750427f, 45.474411f}, @@ -175,7 +206,7 @@ float ThirdPortalWPs [8][3] = //{1825.736084f, 807.305847f, 44.363785f} }; -float FourthPortalWPs [9][3] = +G3D::Vector3 const FourthPortalWPs[9] = { {1921.658447f, 761.657043f, 50.866741f}, {1910.559814f, 755.780457f, 47.701447f}, @@ -189,7 +220,7 @@ float FourthPortalWPs [9][3] = //{1827.100342f, 801.605957f, 44.363358f} }; -float FifthPortalWPs [6][3] = +G3D::Vector3 const FifthPortalWPs[6] = { {1887.398804f, 763.633240f, 47.666851f}, {1879.020386f, 775.396973f, 38.705990f}, @@ -200,7 +231,7 @@ float FifthPortalWPs [6][3] = //{1827.100342f, 801.605957f, 44.363358f} }; -float SixthPoralWPs [4][3] = +G3D::Vector3 const SixthPoralWPs[4] = { {1888.861084f, 805.074768f, 38.375790f}, {1869.793823f, 804.135804f, 38.647018f}, @@ -209,1308 +240,1188 @@ float SixthPoralWPs [4][3] = //{1826.889648f, 803.929993f, 44.363239f} }; -const float SaboteurFinalPos1[3][3] = +G3D::Vector3 const DefaultPortalWPs[1] = { - {1892.502319f, 777.410767f, 38.630402f}, - {1891.165161f, 762.969421f, 47.666920f}, - {1893.168091f, 740.919189f, 47.666920f} + { 1843.567017f, 804.288208f, 44.139091f } }; -const float SaboteurFinalPos2[3][3] = + +uint32 const SaboteurMoraggPathSize = 5; +G3D::Vector3 const SaboteurMoraggPath[SaboteurMoraggPathSize] = // sniff { - {1882.242676f, 834.818726f, 38.646786f}, - {1879.220825f, 842.224854f, 43.333641f}, - {1873.842896f, 863.892456f, 43.333641f} + { 1886.251f, 803.0743f, 38.42326f }, + { 1885.71f, 799.8929f, 38.37241f }, + { 1889.505f, 762.3288f, 47.66684f }, + { 1894.542f, 742.1829f, 47.66684f }, + { 1894.603f, 739.9231f, 47.66684f }, }; -const float SaboteurFinalPos3[2][3] = + +uint32 const SaboteurErekemPathSize = 5; +G3D::Vector3 const SaboteurErekemPath[SaboteurErekemPathSize] = // sniff { - {1904.298340f, 792.400391f, 38.646782f}, - {1935.716919f, 758.437073f, 30.627895f} + { 1886.251f, 803.0743f, 38.42326f }, + { 1881.047f, 829.6866f, 38.64856f }, + { 1877.585f, 844.6685f, 38.49014f }, + { 1876.085f, 851.6685f, 42.99014f }, + { 1873.747f, 864.1373f, 43.33349f } }; -const float SaboteurFinalPos4[3] = + +uint32 const SaboteurIchoronPathSize = 3; +G3D::Vector3 const SaboteurIchoronPath[SaboteurIchoronPathSize] = // sniff { - 1855.006104f, 760.641724f, 38.655266f + { 1886.251f, 803.0743f, 38.42326f }, + { 1888.672f, 801.2348f, 38.42305f }, + { 1901.987f, 793.3254f, 38.65126f } }; -const float SaboteurFinalPos5[3] = + +uint32 const SaboteurLavanthorPathSize = 3; +G3D::Vector3 const SaboteurLavanthorPath[SaboteurLavanthorPathSize] = // sniff { - 1906.667358f, 841.705566f, 38.637894f + { 1886.251f, 803.0743f, 38.42326f }, + { 1867.925f, 778.8035f, 38.64702f }, + { 1853.304f, 759.0161f, 38.65761f } }; -const float SaboteurFinalPos6[5][3] = -{ - {1911.437012f, 821.289246f, 38.684128f}, - {1920.734009f, 822.978027f, 41.525414f}, - {1928.262939f, 830.836609f, 44.668266f}, - {1929.338989f, 837.593933f, 47.137596f}, - {1931.063354f, 848.468445f, 47.190434f} - }; - -const Position PortalLocation[] = + +uint32 const SaboteurXevozzPathSize = 4; +G3D::Vector3 const SaboteurXevozzPath[SaboteurXevozzPathSize] = // sniff { - { 1877.51f, 850.104f, 44.6599f, 4.7822f }, // WP 1 - { 1936.07f, 803.198f, 53.3749f, 3.12414f }, // WP 3 - { 1890.64f, 753.471f, 48.7224f, 1.71042f }, // WP 5 + { 1886.251f, 803.0743f, 38.42326f }, + { 1889.096f, 810.0487f, 38.43871f }, + { 1896.547f, 823.5473f, 38.72863f }, + { 1906.666f, 842.3111f, 38.63351f } }; -#define MAX_PRE_EVENT_PORTAL 3 +uint32 const SaboteurZuramatPathSize = 7; +G3D::Vector3 const SaboteurZuramatPath[SaboteurZuramatPathSize] = // sniff +{ + { 1886.251f, 803.0743f, 38.42326f }, + { 1889.69f, 807.0032f, 38.39914f }, + { 1906.91f, 818.2574f, 38.86596f }, + { 1929.03f, 824.2713f, 46.09165f }, + { 1928.441f, 842.8891f, 47.15078f }, + { 1927.454f, 851.6091f, 47.19094f }, + { 1927.947f, 852.2986f, 47.19637f } +}; -ObjectGuid preEventPortalGUID[MAX_PRE_EVENT_PORTAL] = { ObjectGuid::Empty }; +Position const SinclariPositions[] = // sniff +{ + { 1829.142f, 798.219f, 44.36212f, 0.122173f }, // 0 - Crystal + { 1820.12f, 803.916f, 44.36466f, 0.0f }, // 1 - Outside + { 1816.185f, 804.0629f, 44.44799f, 3.176499f }, // 2 - Second Spawn Point + { 1827.886f, 804.0555f, 44.36467f, 0.0f } // 3 - Outro +}; -const Position MovePosition = { 1806.955566f, 803.851807f, 44.363323f, 0.0f }; -const Position playerTeleportPosition = { 1830.531006f, 803.939758f, 44.340508f, 6.281611f }; -const Position sinclariOutsidePosition = { 1820.429810f, 804.066040f, 44.363998f, 0.0f }; -const Position sinclariCrystalPosition = { 1828.868286f, 798.468811f, 44.363998f, 3.890467f }; +Position const GuardsMovePosition = { 1802.099f, 803.7724f, 44.36466f, 0.0f }; // sniff class npc_sinclari_vh : public CreatureScript { -public: - npc_sinclari_vh() : CreatureScript("npc_sinclari_vh") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF+1: - player->CLOSE_GOSSIP_MENU(); - ENSURE_AI(npc_sinclari_vh::npc_sinclariAI, creature->AI())->uiPhase = 1; - if (InstanceScript* instance = creature->GetInstanceScript()) - instance->SetData(DATA_MAIN_EVENT_PHASE, SPECIAL); - break; - case GOSSIP_ACTION_INFO_DEF+2: - player->SEND_GOSSIP_MENU(13854, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+3: - player->NearTeleportTo(playerTeleportPosition.GetPositionX(), playerTeleportPosition.GetPositionY(), playerTeleportPosition.GetPositionZ(), playerTeleportPosition.GetOrientation(), true); - player->CLOSE_GOSSIP_MENU(); - break; - } - return true; - } + public: + npc_sinclari_vh() : CreatureScript("npc_sinclari_vh") { } - bool OnGossipHello(Player* player, Creature* creature) override - { - if (InstanceScript* instance = creature->GetInstanceScript()) + bool OnGossipHello(Player* player, Creature* creature) override { - switch (instance->GetData(DATA_MAIN_EVENT_PHASE)) + // override default gossip + if (InstanceScript* instance = creature->GetInstanceScript()) { - case NOT_STARTED: - case FAIL: // Allow to start event if not started or wiped - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_START_EVENT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - player->SEND_GOSSIP_MENU(13853, creature->GetGUID()); - break; - case IN_PROGRESS: // Allow to teleport inside if event is in progress - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_I_WANT_IN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); - player->SEND_GOSSIP_MENU(13853, creature->GetGUID()); - break; - default: - player->SEND_GOSSIP_MENU(13910, creature->GetGUID()); + switch (instance->GetData(DATA_MAIN_EVENT_STATE)) + { + case IN_PROGRESS: + player->PrepareGossipMenu(creature, GOSSIP_MENU_SEND_ME_IN, true); + player->SendPreparedGossip(creature); + return true; + case DONE: + return true; // NYI + case NOT_STARTED: + case FAIL: + default: + break; + } } - } - return true; - } - struct npc_sinclariAI : public ScriptedAI - { - npc_sinclariAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); + // load default gossip + return false; } - void Initialize() + struct npc_sinclariAI : public ScriptedAI { - uiPhase = 0; - uiTimer = 0; - } + npc_sinclariAI(Creature* creature) : ScriptedAI(creature), _summons(creature) + { + _instance = creature->GetInstanceScript(); + } - InstanceScript* instance; + void Reset() override + { + _summons.DespawnAll(); + for (uint8 i = 0; i < PortalIntroCount; ++i) + if (Creature* summon = me->SummonCreature(NPC_TELEPORTATION_PORTAL_INTRO, PortalIntroPositions[i], TEMPSUMMON_MANUAL_DESPAWN)) + summon->AI()->SetData(DATA_PORTAL_LOCATION, i); - uint8 uiPhase; - uint32 uiTimer; + me->SetVisible(true); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - void Reset() override - { - Initialize(); + std::list<Creature*> guardList; + me->GetCreatureListWithEntryInGrid(guardList, NPC_VIOLET_HOLD_GUARD, 100.0f); + for (Creature* guard : guardList) + { + guard->Respawn(true); + guard->SetVisible(true); + guard->SetReactState(REACT_AGGRESSIVE); + guard->AI()->EnterEvadeMode(); + } + } - me->SetReactState(REACT_AGGRESSIVE); - for (uint8 i = 0; i < MAX_PRE_EVENT_PORTAL; i++) - if (TempSummon* summon = me->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocation[i], TEMPSUMMON_MANUAL_DESPAWN)) - preEventPortalGUID[i] = summon->GetGUID(); + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override + { + if (menuId == GOSSIP_MENU_START_ENCOUNTER && gossipListId == 0) + { + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + _instance->SetData(DATA_MAIN_EVENT_STATE, SPECIAL); + ScheduleIntro(); + player->PlayerTalkClass->SendCloseGossip(); + } + else if (menuId == GOSSIP_MENU_SEND_ME_IN && gossipListId == 0) + { + me->CastSpell(player, SPELL_TELEPORT_PLAYER, true); + player->PlayerTalkClass->SendCloseGossip(); + } + } - std::list<Creature*> GuardList; - me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); - if (!GuardList.empty()) + void DoAction(int32 actionId) override { - for (std::list<Creature*>::const_iterator itr = GuardList.begin(); itr != GuardList.end(); ++itr) + if (actionId == ACTION_SINCLARI_OUTRO) { - if (Creature* pGuard = *itr) - { - pGuard->DisappearAndDie(); - pGuard->Respawn(); - pGuard->SetVisible(true); - pGuard->SetReactState(REACT_AGGRESSIVE); - } + me->SetVisible(true); + ScheduleOutro(); } } - } - void UpdateAI(uint32 uiDiff) override - { - if (uiPhase) + void UpdateAI(uint32 diff) override { - if (uiTimer <= uiDiff) + _scheduler.Update(diff); + + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + + void ScheduleIntro() + { + _scheduler.Schedule(Seconds(1), [this](TaskContext task) { - switch (uiPhase) + switch (task.GetRepeatCounter()) { - case 1: + case 0: me->SetWalk(true); - me->GetMotionMaster()->MovePoint(0, sinclariCrystalPosition); - uiTimer = 1000; - uiPhase = 6; + me->GetMotionMaster()->MovePoint(0, SinclariPositions[0]); + task.Repeat(Seconds(1)); break; - case 2: - { - me->SetFacingTo(me->GetOrientation() - 3.14f); - Talk(SAY_SINCLARI_1); - uiTimer = 1500; - uiPhase = 7; + case 1: + me->HandleEmoteCommand(EMOTE_ONESHOT_USE_STANDING); + me->GetMap()->SummonCreature(NPC_DEFENSE_SYSTEM, DefenseSystemLocation); + task.Repeat(Seconds(3)); break; - } - case 3: - { - std::list<Creature*> GuardList; - me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); - if (!GuardList.empty()) - for (std::list<Creature*>::const_iterator itr = GuardList.begin(); itr != GuardList.end(); ++itr) + case 2: + me->SetFacingTo(SinclariPositions[0].GetOrientation()); + Talk(SAY_SINCLARI_INTRO_1); + + task.Schedule(Seconds(1), [this](TaskContext /*task*/) + { + std::list<Creature*> guardList; + me->GetCreatureListWithEntryInGrid(guardList, NPC_VIOLET_HOLD_GUARD, 100.0f); + for (Creature* guard : guardList) { - if (Creature* pGuard = *itr) - { - pGuard->SetVisible(false); - } + guard->SetReactState(REACT_PASSIVE); + guard->SetWalk(false); + guard->GetMotionMaster()->MovePoint(0, GuardsMovePosition); } - uiTimer = 2000; - uiPhase = 4; + }); + + task.Repeat(Seconds(2)); + break; + case 3: + me->GetMotionMaster()->MovePoint(0, SinclariPositions[1]); + _summons.DespawnAll(); + task.Repeat(Seconds(5)); break; - } case 4: - me->GetMotionMaster()->MovePoint(0, sinclariOutsidePosition); - uiTimer = 4000; - uiPhase = 5; + me->SetFacingTo(SinclariPositions[1].GetOrientation()); + + task.Schedule(Seconds(1), [this](TaskContext /*task*/) + { + std::list<Creature*> guardList; + me->GetCreatureListWithEntryInGrid(guardList, NPC_VIOLET_HOLD_GUARD, 100.0f); + for (Creature* guard : guardList) + guard->SetVisible(false); + }); + + task.Repeat(Seconds(6)); break; case 5: - me->SetFacingTo(0.006673f); - me->Say(SAY_EVENT_LOCK, LANG_UNIVERSAL, me); // need to change to db say - me->SetReactState(REACT_PASSIVE); - uiTimer = 3000; - uiPhase = 8; + Talk(SAY_SINCLARI_INTRO_2); + task.Repeat(Seconds(4)); break; case 6: - me->GetMotionMaster()->MovementExpired(); - me->HandleEmoteCommand(EMOTE_STATE_USE_STANDING); - uiTimer = 2000; - uiPhase = 2; + me->HandleEmoteCommand(EMOTE_ONESHOT_TALK_NO_SHEATHE); + task.Repeat(Seconds(1)); break; case 7: - { - std::list<Creature*> creatures; - GetCreatureListWithEntryInGrid(creatures, me, NPC_TELEPORTATION_PORTAL, 200.0f); - GetCreatureListWithEntryInGrid(creatures, me, NPC_AZURE_BINDER_1, 200.0f); - GetCreatureListWithEntryInGrid(creatures, me, NPC_AZURE_MAGE_SLAYER_1, 200.0f); - GetCreatureListWithEntryInGrid(creatures, me, NPC_AZURE_INVADER_1, 200.0f); - DoCast(SPELL_CRYSTAL_ACTIVATION); - if (!creatures.empty()) + if (GameObject* mainDoor = _instance->GetGameObject(DATA_MAIN_DOOR)) { - for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr) - (*itr)->DisappearAndDie(); + mainDoor->SetGoState(GO_STATE_READY); + mainDoor->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); } - uiTimer = 500; - uiPhase = 9; - } - break; + task.Repeat(Seconds(5)); + break; case 8: - instance->SetData(DATA_MAIN_EVENT_PHASE, IN_PROGRESS); - uiTimer = 0; - uiPhase = 0; + me->SetVisible(false); + task.Repeat(Seconds(1)); break; case 9: - { - std::list<Creature*> GuardList; - me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); - if (!GuardList.empty()) - for (std::list<Creature*>::const_iterator itr = GuardList.begin(); itr != GuardList.end(); ++itr) - { - if (Creature* pGuard = *itr) - { - pGuard->SetReactState(REACT_PASSIVE); - pGuard->SetWalk(false); - pGuard->GetMotionMaster()->MovePoint(0, MovePosition); - } - } - uiTimer = 4000; - uiPhase = 3; - } - break; + _instance->SetData(DATA_MAIN_EVENT_STATE, IN_PROGRESS); + // [1] GUID: Full: 0xF1300077C202E6DD Type: Creature Entry: 30658 Low: 190173 + break; + default: + break; } - } - else uiTimer -= uiDiff; + }); } - if (!UpdateVictim()) - return; + void ScheduleOutro() + { + _scheduler.Schedule(Seconds(4), [this](TaskContext task) + { + Talk(SAY_SINCLARI_OUTRO); + me->GetMotionMaster()->MovePoint(0, SinclariPositions[3]); - DoMeleeAttackIfReady(); - } - }; + task.Schedule(Seconds(10), [this](TaskContext /*task*/) + { + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + }); + }); + } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_sinclariAI>(creature); - } -}; + void JustSummoned(Creature* summon) override + { + ScriptedAI::JustSummoned(summon); + _summons.Summon(summon); + } -class npc_azure_saboteur : public CreatureScript -{ -public: - npc_azure_saboteur() : CreatureScript("npc_azure_saboteur") { } + void SummonedCreatureDespawn(Creature* summon) override + { + _summons.Despawn(summon); + ScriptedAI::SummonedCreatureDespawn(summon); + } - struct npc_azure_saboteurAI : public npc_escortAI - { - npc_azure_saboteurAI(Creature* creature) : npc_escortAI(creature) - { - instance = creature->GetInstanceScript(); - bHasGotMovingPoints = false; - uiBoss = 0; - Reset(); - } + private: + InstanceScript* _instance; + TaskScheduler _scheduler; - InstanceScript* instance; - bool bHasGotMovingPoints; - uint32 uiBoss; + SummonList _summons; + }; - void Reset() override + CreatureAI* GetAI(Creature* creature) const override { - if (!uiBoss) - uiBoss = instance->GetData(DATA_WAVE_COUNT) == 6 ? instance->GetData(DATA_FIRST_BOSS) : instance->GetData(DATA_SECOND_BOSS); - me->SetReactState(REACT_PASSIVE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + return GetVioletHoldAI<npc_sinclariAI>(creature); } +}; - void WaypointReached(uint32 waypointId) override - { - switch (uiBoss) - { - case 1: - if (waypointId == 2) - FinishPointReached(); - break; - case 2: - if (waypointId == 2) - FinishPointReached(); - break; - case 3: - if (waypointId == 1) - FinishPointReached(); - break; - case 4: - if (waypointId == 0) - FinishPointReached(); - break; - case 5: - if (waypointId == 0) - FinishPointReached(); - break; - case 6: - if (waypointId == 4) - FinishPointReached(); - break; - } - } +class npc_azure_saboteur : public CreatureScript +{ + public: + npc_azure_saboteur() : CreatureScript("npc_azure_saboteur") { } - void UpdateAI(uint32 diff) override + struct npc_azure_saboteurAI : public ScriptedAI { - if (instance->GetData(DATA_MAIN_EVENT_PHASE) != IN_PROGRESS) - me->CastStop(); + npc_azure_saboteurAI(Creature* creature) : ScriptedAI(creature) + { + _instance = creature->GetInstanceScript(); - npc_escortAI::UpdateAI(diff); + if (_instance->GetData(DATA_WAVE_COUNT) == 6) + _bossId = _instance->GetData(DATA_1ST_BOSS); + else + _bossId = _instance->GetData(DATA_2ND_BOSS); + } - if (!bHasGotMovingPoints) + void StartMovement() { - bHasGotMovingPoints = true; - switch (uiBoss) + uint32 pathSize = 0; + G3D::Vector3 const* path = nullptr; + + switch (_bossId) { - case 1: - for (int i=0;i<3;i++) - AddWaypoint(i, SaboteurFinalPos1[i][0], SaboteurFinalPos1[i][1], SaboteurFinalPos1[i][2], 0); - me->SetHomePosition(SaboteurFinalPos1[2][0], SaboteurFinalPos1[2][1], SaboteurFinalPos1[2][2], 4.762346f); + case DATA_MORAGG: + pathSize = SaboteurMoraggPathSize; + path = SaboteurMoraggPath; break; - case 2: - for (int i=0;i<3;i++) - AddWaypoint(i, SaboteurFinalPos2[i][0], SaboteurFinalPos2[i][1], SaboteurFinalPos2[i][2], 0); - me->SetHomePosition(SaboteurFinalPos2[2][0], SaboteurFinalPos2[2][1], SaboteurFinalPos2[2][2], 1.862674f); + case DATA_EREKEM: + pathSize = SaboteurErekemPathSize; + path = SaboteurErekemPath; break; - case 3: - for (int i=0;i<2;i++) - AddWaypoint(i, SaboteurFinalPos3[i][0], SaboteurFinalPos3[i][1], SaboteurFinalPos3[i][2], 0); - me->SetHomePosition(SaboteurFinalPos3[1][0], SaboteurFinalPos3[1][1], SaboteurFinalPos3[1][2], 5.500638f); + case DATA_ICHORON: + pathSize = SaboteurIchoronPathSize; + path = SaboteurIchoronPath; break; - case 4: - AddWaypoint(0, SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 0); - me->SetHomePosition(SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 3.991108f); + case DATA_LAVANTHOR: + pathSize = SaboteurLavanthorPathSize; + path = SaboteurLavanthorPath; break; - case 5: - AddWaypoint(0, SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 0); - me->SetHomePosition(SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 1.100841f); + case DATA_XEVOZZ: + pathSize = SaboteurXevozzPathSize; + path = SaboteurXevozzPath; break; - case 6: - for (int i=0;i<5;i++) - AddWaypoint(i, SaboteurFinalPos6[i][0], SaboteurFinalPos6[i][1], SaboteurFinalPos6[i][2], 0); - me->SetHomePosition(SaboteurFinalPos6[4][0], SaboteurFinalPos6[4][1], SaboteurFinalPos6[4][2], 0.983031f); + case DATA_ZURAMAT: + pathSize = SaboteurZuramatPathSize; + path = SaboteurZuramatPath; break; } - SetDespawnAtEnd(false); - Start(true, true); + if (path) + me->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, path, pathSize, false); + } + + void Reset() override + { + _scheduler.CancelAll(); + _scheduler.Schedule(Seconds(2), [this](TaskContext /*task*/) + { + StartMovement(); + }); + } + + void MovementInform(uint32 type, uint32 pointId) override + { + if (type == EFFECT_MOTION_TYPE && pointId == POINT_INTRO) + { + _scheduler.Schedule(Seconds(0), [this](TaskContext task) + { + me->CastSpell(me, SPELL_SHIELD_DISRUPTION, false); + + if (task.GetRepeatCounter() < 2) + task.Repeat(Seconds(1)); + else + { + task.Schedule(Seconds(2), [this](TaskContext /*task*/) + { + _instance->SetData(DATA_START_BOSS_ENCOUNTER, 1); + me->CastSpell(me, SPELL_TELEPORT_VISUAL, false); + me->DespawnOrUnsummon(1000); + }); + } + }); + } + } + + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); } - } - void FinishPointReached() + private: + InstanceScript* _instance; + TaskScheduler _scheduler; + + uint32 _bossId; + }; + + CreatureAI* GetAI(Creature* creature) const override { - me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false); - me->DisappearAndDie(); - if (Creature* pSaboPort = ObjectAccessor::GetCreature((*me), instance->GetGuidData(DATA_SABOTEUR_PORTAL))) - pSaboPort->DisappearAndDie(); - instance->SetData(DATA_START_BOSS_ENCOUNTER, 1); + return GetVioletHoldAI<npc_azure_saboteurAI>(creature); } - }; +}; - CreatureAI* GetAI(Creature* creature) const override +struct npc_violet_hold_teleportation_portal_commonAI : public ScriptedAI +{ + npc_violet_hold_teleportation_portal_commonAI(Creature* creature) : ScriptedAI(creature), _summons(me) { - return GetInstanceAI<npc_azure_saboteurAI>(creature); + _instance = creature->GetInstanceScript(); + _portalLocation = 0; } -}; -class npc_teleportation_portal_vh : public CreatureScript -{ -public: - npc_teleportation_portal_vh() : CreatureScript("npc_teleportation_portal_vh") { } + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + ScheduleTasks(); + } - struct npc_teleportation_portalAI : public ScriptedAI + void SetData(uint32 type, uint32 data) override { - npc_teleportation_portalAI(Creature* creature) : ScriptedAI(creature), listOfMobs(me) - { - Initialize(); - instance = creature->GetInstanceScript(); - uiTypeOfMobsPortal = urand(0, 1); // 0 - elite mobs 1 - portal guardian or portal keeper with regular mobs + if (type == DATA_PORTAL_LOCATION) + _portalLocation = uint8(data); + } - if (instance->GetData(DATA_MAIN_EVENT_PHASE) == NOT_STARTED) - uiTypeOfMobsPortal = 2; - } + void MoveInLineOfSight(Unit* /*who*/) override { } - void Initialize() - { - uiSpawnTimer = 10000; - bPortalGuardianOrKeeperOrEliteSpawn = false; - } + void EnterCombat(Unit* /*who*/) override { } - uint32 uiSpawnTimer; - bool bPortalGuardianOrKeeperOrEliteSpawn; - uint8 uiTypeOfMobsPortal; + void JustSummoned(Creature* summon) override + { + _summons.Summon(summon); + summon->AI()->SetData(DATA_PORTAL_LOCATION, _portalLocation); + } - SummonList listOfMobs; + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + { + _summons.Despawn(summon); + } - InstanceScript* instance; + virtual void ScheduleTasks() { } - void Reset() override - { - Initialize(); - } + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + } - void EnterCombat(Unit* /*who*/) override { } +protected: + InstanceScript* _instance; + SummonList _summons; + TaskScheduler _scheduler; + uint8 _portalLocation; +}; - void MoveInLineOfSight(Unit* /*who*/) override { } +class npc_violet_hold_teleportation_portal : public CreatureScript +{ + public: + npc_violet_hold_teleportation_portal() : CreatureScript("npc_violet_hold_teleportation_portal") { } - void UpdateAI(uint32 diff) override + struct npc_violet_hold_teleportation_portalAI : public npc_violet_hold_teleportation_portal_commonAI { - if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) + npc_violet_hold_teleportation_portalAI(Creature* creature) : npc_violet_hold_teleportation_portal_commonAI(creature) { - if (instance->GetData(DATA_REMOVE_NPC) == 1) - { - me->DespawnOrUnsummon(); - instance->SetData(DATA_REMOVE_NPC, 0); - } } - uint8 uiWaveCount = instance->GetData(DATA_WAVE_COUNT); - if ((uiWaveCount == 6) || (uiWaveCount == 12)) //Don't spawn mobs on boss encounters - return; + void InitializeAI() override + { + npc_violet_hold_teleportation_portal_commonAI::InitializeAI(); + me->CastSpell(me, SPELL_PORTAL_PERIODIC, true); + } - switch (uiTypeOfMobsPortal) + void SetData(uint32 type, uint32 data) override { - // spawn elite mobs and then set portals visibility to make it look like it dissapeard - case 0: - if (!bPortalGuardianOrKeeperOrEliteSpawn) + npc_violet_hold_teleportation_portal_commonAI::SetData(type, data); + + if (type == DATA_PORTAL_PERIODIC_TICK) + { + if (data == 1) { - if (uiSpawnTimer <= diff) + uint32 entry = RAND(NPC_PORTAL_GUARDIAN, NPC_PORTAL_KEEPER); + if (Creature* portalKeeper = DoSummon(entry, me, 2.0f, 0, TEMPSUMMON_DEAD_DESPAWN)) + me->CastSpell(portalKeeper, SPELL_PORTAL_CHANNEL, false); + + if (Creature* sinclariTrigger = _instance->GetCreature(DATA_SINCLARI_TRIGGER)) { - bPortalGuardianOrKeeperOrEliteSpawn = true; - uint8 k = uiWaveCount < 12 ? 2 : 3; - for (uint8 i = 0; i < k; ++i) - { - uint32 entry = RAND(NPC_AZURE_CAPTAIN, NPC_AZURE_RAIDER, NPC_AZURE_STALKER, NPC_AZURE_SORCEROR); - DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); - } - me->SetVisible(false); - } else uiSpawnTimer -= diff; + if (entry == NPC_PORTAL_GUARDIAN) + sinclariTrigger->AI()->Talk(SAY_SINCLARI_PORTAL_GUARDIAN); + else if (entry == NPC_PORTAL_KEEPER) + sinclariTrigger->AI()->Talk(SAY_SINCLARI_PORTAL_KEEPER); + } } else { - // if all spawned elites have died kill portal - if (listOfMobs.empty()) + uint8 k = _instance->GetData(DATA_WAVE_COUNT) < 12 ? 3 : 4; + while (k--) { - me->Kill(me, false); - me->RemoveCorpse(); + uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_INVADER_2, NPC_AZURE_SPELLBREAKER_1, NPC_AZURE_SPELLBREAKER_2, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_MAGE_SLAYER_2, NPC_AZURE_BINDER_1, NPC_AZURE_BINDER_2); + DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); } } - break; - // spawn portal guardian or portal keeper with regular mobs - case 1: - if (uiSpawnTimer <= diff) - { - if (bPortalGuardianOrKeeperOrEliteSpawn) - { - uint8 k = instance->GetData(DATA_WAVE_COUNT) < 12 ? 3 : 4; - for (uint8 i = 0; i < k; ++i) - { - uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_INVADER_2, NPC_AZURE_SPELLBREAKER_1, NPC_AZURE_SPELLBREAKER_2, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_MAGE_SLAYER_2, NPC_AZURE_BINDER_1, NPC_AZURE_BINDER_2); - DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); - } - } - else - { - bPortalGuardianOrKeeperOrEliteSpawn = true; - uint32 entry = RAND(NPC_PORTAL_GUARDIAN, NPC_PORTAL_KEEPER); - if (Creature* pPortalKeeper = DoSummon(entry, me, 2.0f, 0, TEMPSUMMON_DEAD_DESPAWN)) - me->CastSpell(pPortalKeeper, SPELL_PORTAL_CHANNEL, false); - } - uiSpawnTimer = SPAWN_TIME; - } else uiSpawnTimer -= diff; + } + } - if (bPortalGuardianOrKeeperOrEliteSpawn && !me->IsNonMeleeSpellCast(false)) + void SummonedCreatureDies(Creature* summon, Unit* killer) override + { + npc_violet_hold_teleportation_portal_commonAI::SummonedCreatureDies(summon, killer); + + if (summon->GetEntry() == NPC_PORTAL_GUARDIAN || summon->GetEntry() == NPC_PORTAL_KEEPER) + { + _instance->SetData(DATA_WAVE_COUNT, _instance->GetData(DATA_WAVE_COUNT) + 1); + me->DespawnOrUnsummon(); + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<npc_violet_hold_teleportation_portalAI>(creature); + } +}; + +class npc_violet_hold_teleportation_portal_elite : public CreatureScript +{ + public: + npc_violet_hold_teleportation_portal_elite() : CreatureScript("npc_violet_hold_teleportation_portal_elite") { } + + struct npc_violet_hold_teleportation_portal_eliteAI : public npc_violet_hold_teleportation_portal_commonAI + { + npc_violet_hold_teleportation_portal_eliteAI(Creature* creature) : npc_violet_hold_teleportation_portal_commonAI(creature) + { + } + + void ScheduleTasks() override + { + _scheduler.Schedule(Seconds(15), [this](TaskContext task) + { + uint8 k = _instance->GetData(DATA_WAVE_COUNT) < 12 ? 3 : 4; + while (k--) { - me->Kill(me, false); - me->RemoveCorpse(); + uint32 entry = RAND(NPC_AZURE_CAPTAIN_1, NPC_AZURE_RAIDER_1, NPC_AZURE_STALKER_1, NPC_AZURE_SORCEROR_1); + DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); } - break; - case 2: // Pre-event - if (uiSpawnTimer <= diff) + + if (Creature* sinclariTrigger = _instance->GetCreature(DATA_SINCLARI_TRIGGER)) + sinclariTrigger->AI()->Talk(SAY_SINCLARI_ELITE_SQUAD); + + task.Schedule(Seconds(1), [this](TaskContext /*task*/) { - uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_BINDER_1); - DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); - uiSpawnTimer = SPAWN_TIME; - } else uiSpawnTimer -= diff; - break; + me->SetVisible(false); + }); + }); } - } - void JustDied(Unit* /*killer*/) override + void SummonedCreatureDies(Creature* summon, Unit* killer) override + { + npc_violet_hold_teleportation_portal_commonAI::SummonedCreatureDies(summon, killer); + + if (_summons.empty()) + { + _instance->SetData(DATA_WAVE_COUNT, _instance->GetData(DATA_WAVE_COUNT) + 1); + me->DespawnOrUnsummon(); + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override { - if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) - instance->SetData(DATA_WAVE_COUNT, instance->GetData(DATA_WAVE_COUNT) + 1); + return GetVioletHoldAI<npc_violet_hold_teleportation_portal_eliteAI>(creature); } +}; + +class npc_violet_hold_teleportation_portal_intro : public CreatureScript +{ + public: + npc_violet_hold_teleportation_portal_intro() : CreatureScript("npc_violet_hold_teleportation_portal_intro") { } - void JustSummoned(Creature* summoned) override + struct npc_violet_hold_teleportation_portal_introAI : public npc_violet_hold_teleportation_portal_commonAI { - if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) + npc_violet_hold_teleportation_portal_introAI(Creature* creature) : npc_violet_hold_teleportation_portal_commonAI(creature) { - listOfMobs.Summon(summoned); - instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); } - } - void SummonedCreatureDies(Creature* summoned, Unit* /*killer*/) override - { - if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) + void ScheduleTasks() override { - listOfMobs.Despawn(summoned); - instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID()); + if (_instance->GetData(DATA_MAIN_EVENT_STATE) != NOT_STARTED) + return; + + _scheduler.Schedule(Seconds(15), [this](TaskContext task) + { + uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_BINDER_1); + DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); + + task.Repeat(); + }); } - } - }; + }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_teleportation_portalAI>(creature); - } + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<npc_violet_hold_teleportation_portal_introAI>(creature); + } }; struct violet_hold_trashAI : public npc_escortAI { violet_hold_trashAI(Creature* creature) : npc_escortAI(creature) { - instance = creature->GetInstanceScript(); - bHasGotMovingPoints = false; + _instance = creature->GetInstanceScript(); + _lastWaypointId = 0; - if (instance->GetData(DATA_MAIN_EVENT_PHASE) == NOT_STARTED) - { - if (Creature* portal = me->FindNearestCreature(NPC_TELEPORTATION_PORTAL, 10.0f)) - { - ObjectGuid portalGUID = portal->GetGUID(); - for (uint8 i = 0; i < MAX_PRE_EVENT_PORTAL; i++) - if (portalGUID == preEventPortalGUID[i]) - portalLocationID = i * 2; - } - } - else + SetDespawnAtEnd(false); + + _scheduler.SetValidator([this] { - portalLocationID = instance->GetData(DATA_PORTAL_LOCATION); - Reset(); - } + return !me->HasUnitState(UNIT_STATE_CASTING); + }); } - public: - InstanceScript* instance; - bool bHasGotMovingPoints; - uint32 portalLocationID; - uint32 secondPortalRouteID; - - void WaypointReached(uint32 waypointId) override + void Reset() override { - switch (portalLocationID) - { - case 0: - if (waypointId == 5) - CreatureStartAttackDoor(); - break; - case 1: - if ((waypointId == 8 && secondPortalRouteID == 0) || (waypointId == 7 && secondPortalRouteID == 1)) - CreatureStartAttackDoor(); - break; - case 2: - if (waypointId == 7) - CreatureStartAttackDoor(); - break; - case 3: - if (waypointId == 8) - CreatureStartAttackDoor(); - break; - case 4: - if (waypointId == 5) - CreatureStartAttackDoor(); - break; - case 5: - if (waypointId == 3) - CreatureStartAttackDoor(); - break; - } + _scheduler.CancelAll(); } - void UpdateAI(uint32 diff) override + void SetData(uint32 type, uint32 data) override { - if (instance->GetData(DATA_MAIN_EVENT_PHASE) != IN_PROGRESS) - me->CastStop(); - - if (!bHasGotMovingPoints) + if (type == DATA_PORTAL_LOCATION) { - bHasGotMovingPoints = true; - switch (portalLocationID) + G3D::Vector3 const* path = nullptr; + + switch (data) { case 0: - for (int i=0;i<6;i++) - AddWaypoint(i, FirstPortalWPs[i][0]+irand(-1, 1), FirstPortalWPs[i][1]+irand(-1, 1), FirstPortalWPs[i][2]+irand(-1, 1), 0); - me->SetHomePosition(FirstPortalWPs[5][0], FirstPortalWPs[5][1], FirstPortalWPs[5][2], 3.149439f); + _lastWaypointId = 5; + path = FirstPortalWPs; break; - case 1: - secondPortalRouteID = urand(0, 1); - switch (secondPortalRouteID) + case 7: + switch (urand(0, 1)) { case 0: - for (int i=0;i<9;i++) - AddWaypoint(i, SecondPortalFirstWPs[i][0]+irand(-1, 1), SecondPortalFirstWPs[i][1]+irand(-1, 1), SecondPortalFirstWPs[i][2], 0); - me->SetHomePosition(SecondPortalFirstWPs[8][0]+irand(-1, 1), SecondPortalFirstWPs[8][1]+irand(-1, 1), SecondPortalFirstWPs[8][2]+irand(-1, 1), 3.149439f); + _lastWaypointId = 8; + path = SecondPortalFirstWPs; break; case 1: - for (int i=0;i<8;i++) - AddWaypoint(i, SecondPortalSecondWPs[i][0]+irand(-1, 1), SecondPortalSecondWPs[i][1]+irand(-1, 1), SecondPortalSecondWPs[i][2], 0); - me->SetHomePosition(SecondPortalSecondWPs[7][0], SecondPortalSecondWPs[7][1], SecondPortalSecondWPs[7][2], 3.149439f); + _lastWaypointId = 7; + path = SecondPortalSecondWPs; break; } break; case 2: - for (int i=0;i<8;i++) - AddWaypoint(i, ThirdPortalWPs[i][0]+irand(-1, 1), ThirdPortalWPs[i][1]+irand(-1, 1), ThirdPortalWPs[i][2], 0); - me->SetHomePosition(ThirdPortalWPs[7][0], ThirdPortalWPs[7][1], ThirdPortalWPs[7][2], 3.149439f); + _lastWaypointId = 7; + path = ThirdPortalWPs; break; - case 3: - for (int i=0;i<9;i++) - AddWaypoint(i, FourthPortalWPs[i][0]+irand(-1, 1), FourthPortalWPs[i][1]+irand(-1, 1), FourthPortalWPs[i][2], 0); - me->SetHomePosition(FourthPortalWPs[8][0], FourthPortalWPs[8][1], FourthPortalWPs[8][2], 3.149439f); + case 6: + _lastWaypointId = 8; + path = FourthPortalWPs; break; - case 4: - for (int i=0;i<6;i++) - AddWaypoint(i, FifthPortalWPs[i][0]+irand(-1, 1), FifthPortalWPs[i][1]+irand(-1, 1), FifthPortalWPs[i][2], 0); - me->SetHomePosition(FifthPortalWPs[5][0], FifthPortalWPs[5][1], FifthPortalWPs[5][2], 3.149439f); + case 1: + _lastWaypointId = 5; + path = FifthPortalWPs; break; case 5: - for (int i=0;i<4;i++) - AddWaypoint(i, SixthPoralWPs[i][0]+irand(-1, 1), SixthPoralWPs[i][1]+irand(-1, 1), SixthPoralWPs[i][2], 0); - me->SetHomePosition(SixthPoralWPs[3][0], SixthPoralWPs[3][1], SixthPoralWPs[3][2], 3.149439f); + _lastWaypointId = 3; + path = SixthPoralWPs; break; + default: + _lastWaypointId = 0; + path = DefaultPortalWPs; + break; + } + + if (path) + { + for (uint32 i = 0; i <= _lastWaypointId; i++) + AddWaypoint(i, path[i].x + irand(-1, 1), path[i].y + irand(-1, 1), path[i].z, 0); + me->SetHomePosition(path[_lastWaypointId].x, path[_lastWaypointId].y, path[_lastWaypointId].z, float(M_PI)); } - SetDespawnAtEnd(false); + Start(true, true); } - - npc_escortAI::UpdateAI(diff); } - void JustDied(Unit* /*killer*/) override + void WaypointReached(uint32 waypointId) override { - instance->SetData(DATA_NPC_PRESENCE_AT_DOOR_REMOVE, 1); + if (waypointId == _lastWaypointId) + CreatureStartAttackDoor(); } - void CreatureStartAttackDoor() + void EnterCombat(Unit* who) override { - me->SetReactState(REACT_PASSIVE); - DoCast(SPELL_DESTROY_DOOR_SEAL); - instance->SetData(DATA_NPC_PRESENCE_AT_DOOR_ADD, 1); + npc_escortAI::EnterCombat(who); + ScheduledTasks(); } -}; - -class npc_azure_invader : public CreatureScript -{ -public: - npc_azure_invader() : CreatureScript("npc_azure_invader") { } - struct npc_azure_invaderAI : public violet_hold_trashAI + void UpdateEscortAI(uint32 diff) override { - npc_azure_invaderAI(Creature* creature) : violet_hold_trashAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } + if (_instance->GetData(DATA_MAIN_EVENT_STATE) != IN_PROGRESS) + me->CastStop(); - void Initialize() - { - uiCleaveTimer = 5000; - uiImpaleTimer = 4000; - uiBrutalStrikeTimer = 5000; - uiSunderArmorTimer = 4000; - } + if (!UpdateVictim()) + return; - uint32 uiCleaveTimer; - uint32 uiImpaleTimer; - uint32 uiBrutalStrikeTimer; - uint32 uiSunderArmorTimer; + _scheduler.Update(diff, + std::bind(&npc_escortAI::DoMeleeAttackIfReady, this)); + } - void Reset() override - { - Initialize(); - } + virtual void ScheduledTasks() { } - void UpdateAI(uint32 diff) override - { - violet_hold_trashAI::UpdateAI(diff); + void CreatureStartAttackDoor() + { + me->SetReactState(REACT_DEFENSIVE); + DoCastAOE(SPELL_DESTROY_DOOR_SEAL); + } - if (!UpdateVictim()) - return; +protected: + InstanceScript* _instance; + TaskScheduler _scheduler; - if (me->GetEntry() == NPC_AZURE_INVADER_1) - { - if (uiCleaveTimer <= diff) - { - DoCastVictim(SPELL_CLEAVE); - uiCleaveTimer = 5000; - } else uiCleaveTimer -= diff; + uint32 _lastWaypointId; +}; - if (uiImpaleTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_IMPALE); - uiImpaleTimer = 4000; - } else uiImpaleTimer -= diff; - } +class npc_azure_invader : public CreatureScript +{ + public: + npc_azure_invader() : CreatureScript("npc_azure_invader") { } - if (me->GetEntry() == NPC_AZURE_INVADER_2) + struct npc_azure_invaderAI : public violet_hold_trashAI + { + npc_azure_invaderAI(Creature* creature) : violet_hold_trashAI(creature) { } + + void ScheduledTasks() override { - if (uiBrutalStrikeTimer <= diff) + if (me->GetEntry() == NPC_AZURE_INVADER_1) { - DoCastVictim(SPELL_BRUTAL_STRIKE); - uiBrutalStrikeTimer = 5000; - } else uiBrutalStrikeTimer -= diff; + _scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + DoCastVictim(SPELL_CLEAVE); + task.Repeat(); + }); - if (uiSunderArmorTimer <= diff) + _scheduler.Schedule(Seconds(4), [this](TaskContext task) + { + DoCastVictim(SPELL_IMPALE); + task.Repeat(); + }); + } + else if (me->GetEntry() == NPC_AZURE_INVADER_2) { - DoCastVictim(SPELL_SUNDER_ARMOR); - uiSunderArmorTimer = urand(8000, 10000); - } else uiSunderArmorTimer -= diff; + _scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + DoCastVictim(SPELL_BRUTAL_STRIKE); + task.Repeat(); + }); - DoMeleeAttackIfReady(); + _scheduler.Schedule(Seconds(4), [this](TaskContext task) + { + DoCastVictim(SPELL_SUNDER_ARMOR); + task.Repeat(Seconds(8), Seconds(10)); + }); + } } + }; - DoMeleeAttackIfReady(); + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<npc_azure_invaderAI>(creature); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_invaderAI>(creature); - } }; class npc_azure_binder : public CreatureScript { -public: - npc_azure_binder() : CreatureScript("npc_azure_binder") { } - - struct npc_azure_binderAI : public violet_hold_trashAI - { - npc_azure_binderAI(Creature* creature) : violet_hold_trashAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - uiArcaneExplosionTimer = 5000; - uiArcainBarrageTimer = 4000; - uiFrostNovaTimer = 5000; - uiFrostboltTimer = 4000; - } - - uint32 uiArcaneExplosionTimer; - uint32 uiArcainBarrageTimer; - uint32 uiFrostNovaTimer; - uint32 uiFrostboltTimer; - - void Reset() override - { - Initialize(); - } + public: + npc_azure_binder() : CreatureScript("npc_azure_binder") { } - void UpdateAI(uint32 diff) override + struct npc_azure_binderAI : public violet_hold_trashAI { - violet_hold_trashAI::UpdateAI(diff); + npc_azure_binderAI(Creature* creature) : violet_hold_trashAI(creature) { } - if (!UpdateVictim()) - return; - - if (me->GetEntry() == NPC_AZURE_BINDER_1) + void ScheduledTasks() override { - if (uiArcaneExplosionTimer <= diff) - { - DoCast(SPELL_ARCANE_EXPLOSION); - uiArcaneExplosionTimer = 5000; - } else uiArcaneExplosionTimer -= diff; - - if (uiArcainBarrageTimer <= diff) + if (me->GetEntry() == NPC_AZURE_BINDER_1) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_ARCANE_BARRAGE); - uiArcainBarrageTimer = 6000; - } else uiArcainBarrageTimer -= diff; - } + _scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + DoCastAOE(SPELL_ARCANE_EXPLOSION); + task.Repeat(); + }); - if (me->GetEntry() == NPC_AZURE_BINDER_2) - { - if (uiFrostNovaTimer <= diff) + _scheduler.Schedule(Seconds(4), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f)) + DoCast(target, SPELL_ARCANE_BARRAGE); + task.Repeat(Seconds(6)); + }); + } + else if (me->GetEntry() == NPC_AZURE_BINDER_2) { - DoCast(SPELL_FROST_NOVA); - uiFrostNovaTimer = 5000; - } else uiFrostNovaTimer -= diff; + _scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + DoCastAOE(SPELL_FROST_NOVA); + task.Repeat(); + }); - if (uiFrostboltTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_FROSTBOLT); - uiFrostboltTimer = 6000; - } else uiFrostboltTimer -= diff; + _scheduler.Schedule(Seconds(4), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f)) + DoCast(target, SPELL_FROSTBOLT); + task.Repeat(Seconds(6)); + }); + } } + }; - DoMeleeAttackIfReady(); + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<npc_azure_binderAI>(creature); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_binderAI>(creature); - } }; class npc_azure_mage_slayer : public CreatureScript { -public: - npc_azure_mage_slayer() : CreatureScript("npc_azure_mage_slayer") { } - - struct npc_azure_mage_slayerAI : public violet_hold_trashAI - { - npc_azure_mage_slayerAI(Creature* creature) : violet_hold_trashAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - uiArcaneEmpowermentTimer = 5000; - uiSpellLockTimer = 5000; - } - - uint32 uiArcaneEmpowermentTimer; - uint32 uiSpellLockTimer; - - void Reset() override - { - Initialize(); - } + public: + npc_azure_mage_slayer() : CreatureScript("npc_azure_mage_slayer") { } - void UpdateAI(uint32 diff) override + struct npc_azure_mage_slayerAI : public violet_hold_trashAI { - violet_hold_trashAI::UpdateAI(diff); - - if (!UpdateVictim()) - return; + npc_azure_mage_slayerAI(Creature* creature) : violet_hold_trashAI(creature) { } - if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_1) + void ScheduledTasks() override { - if (uiArcaneEmpowermentTimer <= diff) + if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_1) { - DoCast(me, SPELL_ARCANE_EMPOWERMENT); - uiArcaneEmpowermentTimer = 14000; - } else uiArcaneEmpowermentTimer -= diff; - } - - if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_2) - { - if (uiSpellLockTimer <= diff) + _scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + DoCast(me, SPELL_ARCANE_EMPOWERMENT); + task.Repeat(Seconds(14)); + }); + } + else if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_2) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_SPELL_LOCK); - uiSpellLockTimer = 9000; - } else uiSpellLockTimer -= diff; + _scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + // wrong spellid? + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f)) + DoCast(target, SPELL_SPELL_LOCK); + task.Repeat(Seconds(9)); + }); + } } + }; - DoMeleeAttackIfReady(); + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<npc_azure_mage_slayerAI>(creature); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_mage_slayerAI>(creature); - } }; class npc_azure_raider : public CreatureScript { -public: - npc_azure_raider() : CreatureScript("npc_azure_raider") { } - - struct npc_azure_raiderAI : public violet_hold_trashAI - { - npc_azure_raiderAI(Creature* creature) : violet_hold_trashAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - uiConcussionBlowTimer = 5000; - uiMagicReflectionTimer = 8000; - } - - uint32 uiConcussionBlowTimer; - uint32 uiMagicReflectionTimer; - - void Reset() override - { - Initialize(); - } + public: + npc_azure_raider() : CreatureScript("npc_azure_raider") { } - void UpdateAI(uint32 diff) override + struct npc_azure_raiderAI : public violet_hold_trashAI { - violet_hold_trashAI::UpdateAI(diff); + npc_azure_raiderAI(Creature* creature) : violet_hold_trashAI(creature) { } - if (!UpdateVictim()) - return; - - if (uiConcussionBlowTimer <= diff) + void ScheduledTasks() override { - DoCastVictim(SPELL_CONCUSSION_BLOW); - uiConcussionBlowTimer = 5000; - } else uiConcussionBlowTimer -= diff; + _scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + DoCastVictim(SPELL_CONCUSSION_BLOW); + task.Repeat(); + }); - if (uiMagicReflectionTimer <= diff) - { - DoCast(SPELL_MAGIC_REFLECTION); - uiMagicReflectionTimer = urand(10000, 15000); - } else uiMagicReflectionTimer -= diff; + _scheduler.Schedule(Seconds(8), [this](TaskContext task) + { + DoCast(me, SPELL_MAGIC_REFLECTION); + task.Repeat(Seconds(10), Seconds(15)); + }); + } + }; - DoMeleeAttackIfReady(); + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<npc_azure_raiderAI>(creature); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_raiderAI>(creature); - } }; class npc_azure_stalker : public CreatureScript { -public: - npc_azure_stalker() : CreatureScript("npc_azure_stalker") { } - - struct npc_azure_stalkerAI : public violet_hold_trashAI - { - npc_azure_stalkerAI(Creature* creature) : violet_hold_trashAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - _backstabTimer = 1300; - _tacticalBlinkTimer = 8000; - _tacticalBlinkCast = false; - } - - void Reset() override - { - Initialize(); - } + public: + npc_azure_stalker() : CreatureScript("npc_azure_stalker") { } - void UpdateAI(uint32 diff) override + struct npc_azure_stalkerAI : public violet_hold_trashAI { - violet_hold_trashAI::UpdateAI(diff); - - if (!UpdateVictim()) - return; + npc_azure_stalkerAI(Creature* creature) : violet_hold_trashAI(creature) { } - if (!_tacticalBlinkCast) + void ScheduledTasks() override { - if (_tacticalBlinkTimer <= diff) + _scheduler.Schedule(Seconds(8), [this](TaskContext task) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40, true)) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f)) DoCast(target, SPELL_TACTICAL_BLINK); - _tacticalBlinkTimer = 6000; - _tacticalBlinkCast = true; - } else _tacticalBlinkTimer -= diff; - } - else - { - if (_backstabTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 10, true)) - DoCast(target, SPELL_BACKSTAB); - _tacticalBlinkCast = false; - _backstabTimer =1300; - } else _backstabTimer -= diff; + task.Schedule(Milliseconds(1300), [this](TaskContext /*task*/) + { + if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 5.0f)) + DoCast(target, SPELL_BACKSTAB); + }); + + task.Repeat(); + }); } + }; - DoMeleeAttackIfReady(); + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<npc_azure_stalkerAI>(creature); } - - private: - uint32 _backstabTimer; - uint32 _tacticalBlinkTimer; - bool _tacticalBlinkCast; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_stalkerAI>(creature); - } }; class npc_azure_spellbreaker : public CreatureScript { -public: - npc_azure_spellbreaker() : CreatureScript("npc_azure_spellbreaker") { } + public: + npc_azure_spellbreaker() : CreatureScript("npc_azure_spellbreaker") { } - struct npc_azure_spellbreakerAI : public violet_hold_trashAI - { - npc_azure_spellbreakerAI(Creature* creature) : violet_hold_trashAI(creature) + struct npc_azure_spellbreakerAI : public violet_hold_trashAI { - Initialize(); - instance = creature->GetInstanceScript(); - } + npc_azure_spellbreakerAI(Creature* creature) : violet_hold_trashAI(creature) { } - void Initialize() - { - uiArcaneBlastTimer = 5000; - uiSlowTimer = 4000; - uiChainsOfIceTimer = 5000; - uiConeOfColdTimer = 4000; - } + void ScheduledTasks() override + { + if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_1) + { + _scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f)) + DoCast(target, SPELL_ARCANE_BLAST); + task.Repeat(Seconds(6)); + }); - uint32 uiArcaneBlastTimer; - uint32 uiSlowTimer; - uint32 uiChainsOfIceTimer; - uint32 uiConeOfColdTimer; + _scheduler.Schedule(Seconds(4), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f)) + DoCast(target, SPELL_SLOW); + task.Repeat(Seconds(5)); + }); + } + else if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_2) + { + _scheduler.Schedule(Seconds(5), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f)) + DoCast(target, SPELL_CHAINS_OF_ICE); + task.Repeat(Seconds(7)); + }); - void Reset() override - { - Initialize(); - } + _scheduler.Schedule(Seconds(4), [this](TaskContext task) + { + DoCast(me, SPELL_CONE_OF_COLD); + task.Repeat(Seconds(5)); + }); + } + } + }; - void UpdateAI(uint32 diff) override + CreatureAI* GetAI(Creature* creature) const override { - violet_hold_trashAI::UpdateAI(diff); - - if (!UpdateVictim()) - return; + return GetVioletHoldAI<npc_azure_spellbreakerAI>(creature); + } +}; - if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_1) - { - if (uiArcaneBlastTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_ARCANE_BLAST); - uiArcaneBlastTimer = 6000; - } else uiArcaneBlastTimer -= diff; +class npc_azure_captain : public CreatureScript +{ + public: + npc_azure_captain() : CreatureScript("npc_azure_captain") { } - if (uiSlowTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_SLOW); - uiSlowTimer = 5000; - } else uiSlowTimer -= diff; - } + struct npc_azure_captainAI : public violet_hold_trashAI + { + npc_azure_captainAI(Creature* creature) : violet_hold_trashAI(creature) { } - if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_2) + void ScheduledTasks() override { - if (uiChainsOfIceTimer <= diff) + _scheduler.Schedule(Seconds(5), [this](TaskContext task) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_CHAINS_OF_ICE); - uiChainsOfIceTimer = 7000; - } else uiChainsOfIceTimer -= diff; + DoCastVictim(SPELL_MORTAL_STRIKE); + task.Repeat(); + }); - if (uiConeOfColdTimer <= diff) + _scheduler.Schedule(Seconds(8), [this](TaskContext task) { - DoCast(SPELL_CONE_OF_COLD); - uiConeOfColdTimer = 5000; - } else uiConeOfColdTimer -= diff; + DoCast(me, SPELL_WHIRLWIND_OF_STEEL); + task.Repeat(); + }); } + }; - DoMeleeAttackIfReady(); + CreatureAI* GetAI(Creature* creature) const override + { + return GetVioletHoldAI<npc_azure_captainAI>(creature); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_spellbreakerAI>(creature); - } }; -class npc_azure_captain : public CreatureScript +class npc_azure_sorceror : public CreatureScript { -public: - npc_azure_captain() : CreatureScript("npc_azure_captain") { } + public: + npc_azure_sorceror() : CreatureScript("npc_azure_sorceror") { } - struct npc_azure_captainAI : public violet_hold_trashAI - { - npc_azure_captainAI(Creature* creature) : violet_hold_trashAI(creature) + struct npc_azure_sorcerorAI : public violet_hold_trashAI { - Initialize(); - instance = creature->GetInstanceScript(); - } + npc_azure_sorcerorAI(Creature* creature) : violet_hold_trashAI(creature) { } - void Initialize() - { - uiMortalStrikeTimer = 5000; - uiWhirlwindTimer = 8000; - } + void ScheduledTasks() override + { + _scheduler.Schedule(Seconds(4), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 35.0f)) + DoCast(target, SPELL_ARCANE_STREAM); + task.Repeat(Seconds(5), Seconds(10)); + }); - uint32 uiMortalStrikeTimer; - uint32 uiWhirlwindTimer; + _scheduler.Schedule(Seconds(), Seconds(), [this](TaskContext task) + { + DoCastAOE(SPELL_MANA_DETONATION); + task.Repeat(Seconds(2), Seconds(6)); + }); + } + }; - void Reset() override + CreatureAI* GetAI(Creature* creature) const override { - Initialize(); + return GetVioletHoldAI<npc_azure_sorcerorAI>(creature); } +}; + +class npc_violet_hold_defense_system : public CreatureScript +{ + public: + npc_violet_hold_defense_system() : CreatureScript("npc_violet_hold_defense_system") { } - void UpdateAI(uint32 diff) override + struct npc_violet_hold_defense_systemAI : public ScriptedAI { - violet_hold_trashAI::UpdateAI(diff); + npc_violet_hold_defense_systemAI(Creature* creature) : ScriptedAI(creature) { } - if (!UpdateVictim()) - return; + void Reset() override + { + ScheduledTasks(); + me->DespawnOrUnsummon(7000); + } - if (uiMortalStrikeTimer <= diff) + void ScheduledTasks() { - DoCastVictim(SPELL_MORTAL_STRIKE); - uiMortalStrikeTimer = 5000; - } else uiMortalStrikeTimer -= diff; + _scheduler.Schedule(Seconds(4), [this](TaskContext task) + { + DoCastAOE(SPELL_ARCANE_LIGHTNING_DAMAGE); + DoCastAOE(SPELL_ARCANE_LIGHTNING_DUMMY); + if (task.GetRepeatCounter() == 2) + DoCastAOE(SPELL_ARCANE_LIGHTNING_INSTAKILL); + else + task.Repeat(Seconds(1)); + }); + } - if (uiWhirlwindTimer <= diff) + void UpdateAI(uint32 diff) override { - DoCast(me, SPELL_WHIRLWIND_OF_STEEL); - uiWhirlwindTimer = 8000; - } else uiWhirlwindTimer -= diff; + _scheduler.Update(diff); + } - DoMeleeAttackIfReady(); - } - }; + private: + TaskScheduler _scheduler; + }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_captainAI>(creature); - } + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_violet_hold_defense_systemAI(creature); + } }; -class npc_azure_sorceror : public CreatureScript +class go_activation_crystal : public GameObjectScript { -public: - npc_azure_sorceror() : CreatureScript("npc_azure_sorceror") { } - - struct npc_azure_sorcerorAI : public violet_hold_trashAI - { - npc_azure_sorcerorAI(Creature* creature) : violet_hold_trashAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } + public: + go_activation_crystal() : GameObjectScript("go_activation_crystal") { } - void Initialize() + bool OnGossipHello(Player* player, GameObject* /*go*/) override { - uiArcaneStreamTimer = 4000; - uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer; - uiManaDetonationTimer = 5000; + player->CastSpell(player, SPELL_CRYSTAL_ACTIVATION, true); + return false; } +}; - uint32 uiArcaneStreamTimer; - uint32 uiArcaneStreamTimerStartingValueHolder; - uint32 uiManaDetonationTimer; - - void Reset() override - { - Initialize(); - } +// 58040 - Destroy Door Seal +class spell_violet_hold_destroy_door_seal : public SpellScriptLoader +{ + public: + spell_violet_hold_destroy_door_seal() : SpellScriptLoader("spell_violet_hold_destroy_door_seal") { } - void UpdateAI(uint32 diff) override + class spell_violet_hold_destroy_door_seal_AuraScript : public AuraScript { - violet_hold_trashAI::UpdateAI(diff); + PrepareAuraScript(spell_violet_hold_destroy_door_seal_AuraScript); - if (!UpdateVictim()) - return; - - if (uiArcaneStreamTimer <= diff) + bool Load() override { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_ARCANE_STREAM); - uiArcaneStreamTimer = urand(0, 5000)+5000; - uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer; - } else uiArcaneStreamTimer -= diff; + _instance = GetUnitOwner()->GetInstanceScript(); + return _instance != nullptr; + } - if (uiManaDetonationTimer <= diff && uiArcaneStreamTimer >=1500 && uiArcaneStreamTimer <= uiArcaneStreamTimerStartingValueHolder/2) + void PeriodicTick(AuraEffect const* /*aurEff*/) { - DoCast(SPELL_MANA_DETONATION); - uiManaDetonationTimer = urand(2000, 6000); - } else uiManaDetonationTimer -= diff; - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_sorcerorAI>(creature); - } -}; + PreventDefaultAction(); + if (uint32 integrity = _instance->GetData(DATA_DOOR_INTEGRITY)) + _instance->SetData(DATA_DOOR_INTEGRITY, integrity - 1); + } -class npc_violet_hold_arcane_sphere : public CreatureScript -{ -public: - npc_violet_hold_arcane_sphere() : CreatureScript("npc_violet_hold_arcane_sphere") { } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_violet_hold_destroy_door_seal_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } - struct npc_violet_hold_arcane_sphereAI : public ScriptedAI - { - npc_violet_hold_arcane_sphereAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } + private: + InstanceScript* _instance = nullptr; + }; - void Initialize() + AuraScript* GetAuraScript() const override { - DespawnTimer = 3000; + return new spell_violet_hold_destroy_door_seal_AuraScript(); } +}; - uint32 DespawnTimer; +// 58008 - Portal Periodic +class spell_violet_hold_portal_periodic : public SpellScriptLoader +{ + public: + spell_violet_hold_portal_periodic() : SpellScriptLoader("spell_violet_hold_portal_periodic") { } - void Reset() override + class spell_violet_hold_portal_periodic_AuraScript : public AuraScript { - Initialize(); + PrepareAuraScript(spell_violet_hold_portal_periodic_AuraScript); - me->SetDisableGravity(true); - DoCast(me, SPELL_ARCANE_SPHERE_PASSIVE, true); - } + void PeriodicTick(AuraEffect const* aurEff) + { + PreventDefaultAction(); + if (GetTarget()->IsAIEnabled) + GetTarget()->GetAI()->SetData(DATA_PORTAL_PERIODIC_TICK, aurEff->GetTickNumber()); + } - void EnterCombat(Unit * /*who*/) override { } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_violet_hold_portal_periodic_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; - void UpdateAI(uint32 diff) override + AuraScript* GetAuraScript() const override { - if (DespawnTimer <= diff) - me->Kill(me); - else - DespawnTimer -= diff; + return new spell_violet_hold_portal_periodic_AuraScript(); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_violet_hold_arcane_sphereAI(creature); - } }; -class go_activation_crystal : public GameObjectScript +// 62138 - Teleport to Inside Violet Hold +class spell_violet_hold_teleport_player : public SpellScriptLoader { -public: - go_activation_crystal() : GameObjectScript("go_activation_crystal") { } + public: + spell_violet_hold_teleport_player() : SpellScriptLoader("spell_violet_hold_teleport_player") { } - bool OnGossipHello(Player * /*player*/, GameObject* go) override - { - go->EventInform(EVENT_ACTIVATE_CRYSTAL); - return false; - } -}; + class spell_violet_hold_teleport_player_SpellScript : public SpellScript + { + PrepareSpellScript(spell_violet_hold_teleport_player_SpellScript); -class spell_crystal_activation : public SpellScriptLoader -{ -public: - spell_crystal_activation() : SpellScriptLoader("spell_crystal_activation") { } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_TELEPORT_PLAYER_EFFECT)) + return false; + return true; + } - class spell_crystal_activation_SpellScript : public SpellScript - { - PrepareSpellScript(spell_crystal_activation_SpellScript); + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + target->CastSpell(target, SPELL_TELEPORT_PLAYER_EFFECT, true); + } - void HandleSendEvent(SpellEffIndex effIndex) - { - if (GetHitUnit()->GetEntry() == NPC_VIOLET_HOLD_GUARD) - PreventHitDefaultEffect(effIndex); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_violet_hold_teleport_player_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; - void Register() override + SpellScript* GetSpellScript() const override { - OnEffectHitTarget += SpellEffectFn(spell_crystal_activation_SpellScript::HandleSendEvent, EFFECT_0, SPELL_EFFECT_SEND_EVENT); + return new spell_violet_hold_teleport_player_SpellScript(); } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_crystal_activation_SpellScript(); - } }; void AddSC_violet_hold() { new npc_sinclari_vh(); - new npc_teleportation_portal_vh(); + new npc_violet_hold_teleportation_portal(); + new npc_violet_hold_teleportation_portal_elite(); + new npc_violet_hold_teleportation_portal_intro(); new npc_azure_invader(); new npc_azure_spellbreaker(); new npc_azure_binder(); @@ -1520,7 +1431,9 @@ void AddSC_violet_hold() new npc_azure_raider(); new npc_azure_stalker(); new npc_azure_saboteur(); - new npc_violet_hold_arcane_sphere(); + new npc_violet_hold_defense_system(); new go_activation_crystal(); - new spell_crystal_activation(); + new spell_violet_hold_destroy_door_seal(); + new spell_violet_hold_portal_periodic(); + new spell_violet_hold_teleport_player(); } diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.h b/src/server/scripts/Northrend/VioletHold/violet_hold.h index 2bd90672024..113a3c46ea0 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.h +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.h @@ -15,44 +15,56 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef DEF_VIOLET_HOLD_H -#define DEF_VIOLET_HOLD_H +#ifndef VIOLET_HOLD_H_ +#define VIOLET_HOLD_H_ +#define VioletHoldScriptName "instance_violet_hold" #define DataHeader "VH" -uint32 const EncounterCount = 3; +uint32 const EncounterCount = 3 + 6; + +// Defined in instance_violet_hold.cpp +extern Position const DefenseSystemLocation; +uint8 const PortalIntroCount = 3; +extern Position const PortalIntroPositions[]; + +/* + * Violet hold bosses: + * + * 1 - Moragg + * 2 - Erekem + * 3 - Ichoron + * 4 - Lavanthor + * 5 - Xevozz + * 6 - Zuramat + * 7 - Cyanigosa + */ enum Data { // Main encounters - DATA_1ST_BOSS_EVENT, - DATA_2ND_BOSS_EVENT, - DATA_CYANIGOSA, + DATA_1ST_BOSS = 0, + DATA_2ND_BOSS = 1, + DATA_CYANIGOSA = 2, + // Bosses + DATA_MORAGG = 3, + DATA_EREKEM = 4, + DATA_ICHORON = 5, + DATA_LAVANTHOR = 6, + DATA_XEVOZZ = 7, + DATA_ZURAMAT = 8, // Misc + DATA_MAIN_EVENT_STATE, DATA_WAVE_COUNT, - DATA_REMOVE_NPC, - DATA_PORTAL_LOCATION, DATA_DOOR_INTEGRITY, - DATA_NPC_PRESENCE_AT_DOOR, - DATA_NPC_PRESENCE_AT_DOOR_ADD, - DATA_NPC_PRESENCE_AT_DOOR_REMOVE, + DATA_PORTAL_LOCATION, DATA_START_BOSS_ENCOUNTER, - DATA_FIRST_BOSS, - DATA_SECOND_BOSS, - DATA_ACTIVATE_CRYSTAL, - DATA_MAIN_EVENT_PHASE, DATA_DEFENSELESS, // Bosses - DATA_MORAGG, - DATA_EREKEM, DATA_EREKEM_GUARD_1, DATA_EREKEM_GUARD_2, - DATA_ICHORON, - DATA_LAVANTHOR, - DATA_XEVOZZ, - DATA_ZURAMAT, // Cells DATA_MORAGG_CELL, @@ -67,43 +79,43 @@ enum Data // Misc DATA_MAIN_DOOR, DATA_SINCLARI, - DATA_TELEPORTATION_PORTAL, - DATA_SABOTEUR_PORTAL, - DATA_ADD_TRASH_MOB, - DATA_DEL_TRASH_MOB -}; - -enum Bosses -{ - BOSS_NONE, // 0 used as marker for not yet randomized - BOSS_MORAGG, - BOSS_EREKEM, - BOSS_ICHORON, - BOSS_LAVANTHOR, - BOSS_XEVOZZ, - BOSS_ZURAMAT, - BOSS_CYANIGOSA + DATA_SINCLARI_TRIGGER, + DATA_HANDLE_CELLS }; enum CreaturesIds { - NPC_TELEPORTATION_PORTAL = 31011, + NPC_TELEPORTATION_PORTAL = 30679, + NPC_TELEPORTATION_PORTAL_ELITE = 32174, + NPC_TELEPORTATION_PORTAL_INTRO = 31011, NPC_PORTAL_GUARDIAN = 30660, NPC_PORTAL_KEEPER = 30695, NPC_XEVOZZ = 29266, NPC_LAVANTHOR = 29312, NPC_ICHORON = 29313, + NPC_ICHOR_GLOBULE = 29321, + NPC_ICHORON_SUMMON_TARGET = 29326, NPC_ZURAMAT = 29314, + NPC_VOID_SENTRY = 29364, + NPC_VOID_SENTRY_BALL = 29365, NPC_EREKEM = 29315, NPC_EREKEM_GUARD = 29395, NPC_MORAGG = 29316, + + NPC_DUMMY_XEVOZZ = 32231, + NPC_DUMMY_LAVANTHOR = 32237, + NPC_DUMMY_ICHORON = 32234, + NPC_DUMMY_ZURAMAT = 32230, + NPC_DUMMY_EREKEM = 32226, + NPC_DUMMY_EREKEM_GUARD = 32228, + NPC_DUMMY_MORAGG = 32235, + NPC_CYANIGOSA = 31134, NPC_SINCLARI = 30658, + NPC_SINCLARI_TRIGGER = 32204, NPC_SABOTEOUR = 31079, NPC_VIOLET_HOLD_GUARD = 30659, - NPC_DEFENSE_SYSTEM = 30837, - NPC_VOID_SENTRY = 29364, - NPC_VOID_SENTRY_BALL = 29365 + NPC_DEFENSE_SYSTEM = 30837 }; enum GameObjectIds @@ -117,13 +129,13 @@ enum GameObjectIds GO_EREKEM_GUARD_1_DOOR = 191563, GO_EREKEM_GUARD_2_DOOR = 191562, GO_MORAGG_DOOR = 191606, - GO_INTRO_ACTIVATION_CRYSTAL = 193615, - GO_ACTIVATION_CRYSTAL = 193611 + GO_ACTIVATION_CRYSTAL = 193611, + GO_INTRO_ACTIVATION_CRYSTAL = 193615 }; enum WorldStateIds { - WORLD_STATE_VH = 3816, + WORLD_STATE_VH_SHOW = 3816, WORLD_STATE_VH_PRISON_STATE = 3815, WORLD_STATE_VH_WAVE_COUNT = 3810, }; @@ -133,4 +145,16 @@ enum Events EVENT_ACTIVATE_CRYSTAL = 20001 }; -#endif +enum InstanceMisc +{ + ACTION_SINCLARI_OUTRO = 1, + POINT_INTRO = 1 +}; + +template<class AI> +inline AI* GetVioletHoldAI(Creature* creature) +{ + return GetInstanceAI<AI>(creature, VioletHoldScriptName); +} + +#endif // VIOLET_HOLD_H_ diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index eef993f7a7d..511ddcff5fa 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -1818,7 +1818,7 @@ public: player->FailQuest(QUEST_GET_ME_OUTA_HERE); } - void UpdateEscortAI(const uint32 /*diff*/) override + void UpdateEscortAI(uint32 /*diff*/) override { if (GetAttack() && UpdateVictim()) { diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index fe72a2cedf7..ba69a1385d5 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -98,7 +98,7 @@ public: player->FailQuest(QUEST_TRAIL_OF_FIRE); } - void UpdateEscortAI(const uint32 diff) override + void UpdateEscortAI(uint32 diff) override { if (HealthBelowPct(75)) { diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp index 1e998b78c03..e0c7e4b57a7 100644 --- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp +++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp @@ -621,17 +621,17 @@ public: enum MiscLifewarden { - NPC_PRESENCE = 28563, // Freya's Presence - NPC_SABOTEUR = 28538, // Cultist Saboteur - NPC_SERVANT = 28320, // Servant of Freya + NPC_PRESENCE = 28563, // Freya's Presence + NPC_SABOTEUR = 28538, // Cultist Saboteur + NPC_SERVANT = 28320, // Servant of Freya - WHISPER_ACTIVATE = 0, + WHISPER_ACTIVATE = 0, - SPELL_FREYA_DUMMY = 51318, - SPELL_LIFEFORCE = 51395, - SPELL_FREYA_DUMMY_TRIGGER = 51335, - SPELL_LASHER_EMERGE = 48195, - SPELL_WILD_GROWTH = 52948, + SPELL_FREYA_DUMMY = 51318, + SPELL_LIFEFORCE = 51395, + SPELL_FREYA_DUMMY_TRIGGER = 51335, + SPELL_LASHER_EMERGE = 48195, + SPELL_WILD_GROWTH = 52948, }; class spell_q12620_the_lifewarden_wrath : public SpellScriptLoader @@ -701,25 +701,25 @@ public: enum KickWhatKick { - NPC_LUCKY_WILHELM = 28054, - NPC_APPLE = 28053, - NPC_DROSTAN = 28328, - NPC_CRUNCHY = 28346, - NPC_THICKBIRD = 28093, - - SPELL_HIT_APPLE = 51331, - SPELL_MISS_APPLE = 51332, - SPELL_MISS_BIRD_APPLE = 51366, - SPELL_APPLE_FALL = 51371, - SPELL_BIRD_FALL = 51369, - - EVENT_MISS = 0, - EVENT_HIT = 1, - EVENT_MISS_BIRD = 2, - - SAY_WILHELM_MISS = 0, - SAY_WILHELM_HIT = 1, - SAY_DROSTAN_REPLY_MISS = 0, + NPC_LUCKY_WILHELM = 28054, + NPC_APPLE = 28053, + NPC_DROSTAN = 28328, + NPC_CRUNCHY = 28346, + NPC_THICKBIRD = 28093, + + SPELL_HIT_APPLE = 51331, + SPELL_MISS_APPLE = 51332, + SPELL_MISS_BIRD_APPLE = 51366, + SPELL_APPLE_FALL = 51371, + SPELL_BIRD_FALL = 51369, + + EVENT_MISS = 0, + EVENT_HIT = 1, + EVENT_MISS_BIRD = 2, + + SAY_WILHELM_MISS = 0, + SAY_WILHELM_HIT = 1, + SAY_DROSTAN_REPLY_MISS = 0, }; class spell_q12589_shoot_rjr : public SpellScriptLoader @@ -799,8 +799,6 @@ public: wilhelm->AI()->Talk(SAY_WILHELM_HIT); if (Player* player = shooter->ToPlayer()) player->KilledMonsterCredit(NPC_APPLE); - apple->DespawnOrUnsummon(); - break; } } @@ -827,11 +825,11 @@ may be easily converted to SAI when they get.*/ enum SongOfWindAndWater { // Spells - SPELL_DEVOUR_WIND = 52862, - SPELL_DEVOUR_WATER = 52864, + SPELL_DEVOUR_WIND = 52862, + SPELL_DEVOUR_WATER = 52864, // NPCs - NPC_HAIPHOON_WATER = 28999, - NPC_HAIPHOON_AIR = 28985 + NPC_HAIPHOON_WATER = 28999, + NPC_HAIPHOON_AIR = 28985 }; class npc_haiphoon : public CreatureScript @@ -882,7 +880,7 @@ enum ReconnaissanceFlight VIC_SAY_6 = 6, PLANE_EMOTE = 0, - SPELL_ENGINE = 52255, // Engine on Fire + SPELL_ENGINE = 52255, // Engine on Fire SPELL_LAND = 52226, // Land Flying Machine SPELL_CREDIT = 53328 // Land Flying Machine Credit diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp index 9667b4e3bb0..80cc2028cb3 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp @@ -136,7 +136,7 @@ class boss_ambassador_hellmaw : public CreatureScript Talk(SAY_DEATH); } - void UpdateEscortAI(uint32 const diff) override + void UpdateEscortAI(uint32 diff) override { if (!UpdateVictim()) return; diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp index 1657b178327..d7ba0a34939 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp @@ -275,8 +275,8 @@ class instance_serpent_shrine : public InstanceMapScript if (data == DONE) { HandleGameObject(BridgePart[0], true); - HandleGameObject(BridgePart[0], true); - HandleGameObject(BridgePart[0], true); + HandleGameObject(BridgePart[1], true); + HandleGameObject(BridgePart[2], true); } break; case DATA_TRASH: diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp index 3986e50877f..8e56df071c9 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp @@ -146,8 +146,6 @@ class boss_shattered_executioner : public CreatureScript me->RemoveLootMode(LOOT_MODE_HARD_MODE_2); case 1: me->RemoveLootMode(LOOT_MODE_HARD_MODE_3); - default: - break; } } } diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index d140b41a8f8..8549fe5030f 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -407,10 +407,483 @@ public: } }; +enum ExorcismSpells +{ + SPELL_JULES_GOES_PRONE = 39283, + SPELL_JULES_THREATENS_AURA = 39284, + SPELL_JULES_GOES_UPRIGHT = 39294, + SPELL_JULES_VOMITS_AURA = 39295, + + SPELL_BARADAS_COMMAND = 39277, + SPELL_BARADA_FALTERS = 39278, +}; + +enum ExorcismTexts +{ + SAY_BARADA_1 = 0, + SAY_BARADA_2 = 1, + SAY_BARADA_3 = 2, + SAY_BARADA_4 = 3, + SAY_BARADA_5 = 4, + SAY_BARADA_6 = 5, + SAY_BARADA_7 = 6, + SAY_BARADA_8 = 7, + + SAY_JULES_1 = 0, + SAY_JULES_2 = 1, + SAY_JULES_3 = 2, + SAY_JULES_4 = 3, + SAY_JULES_5 = 4, +}; + +Position const exorcismPos[11] = +{ + { -707.123f, 2751.686f, 101.592f, 4.577416f }, //Barada Waypoint-1 0 + { -710.731f, 2749.075f, 101.592f, 1.513286f }, //Barada Cast position 1 + { -710.332f, 2754.394f, 102.948f, 3.207566f }, //Jules 2 + { -714.261f, 2747.754f, 103.391f, 0.0f }, //Jules Waypoint-1 3 + { -713.113f, 2750.194f, 103.391f, 0.0f }, //Jules Waypoint-2 4 + { -710.385f, 2750.896f, 103.391f, 0.0f }, //Jules Waypoint-3 5 + { -708.309f, 2750.062f, 103.391f, 0.0f }, //Jules Waypoint-4 6 + { -707.401f, 2747.696f, 103.391f, 0.0f }, //Jules Waypoint-5 7 + { -708.591f, 2745.266f, 103.391f, 0.0f }, //Jules Waypoint-6 8 + { -710.597f, 2744.035f, 103.391f, 0.0f }, //Jules Waypoint-7 9 + { -713.089f, 2745.302f, 103.391f, 0.0f }, //Jules Waypoint-8 10 +}; + +enum ExorcismMisc +{ + NPC_DARKNESS_RELEASED = 22507, + NPC_FOUL_PURGE = 22506, + NPC_COLONEL_JULES = 22432, + + BARADAS_GOSSIP_MESSAGE = 10683, + + QUEST_THE_EXORCISM_OF_COLONEL_JULES = 10935, + + ACTION_START_EVENT = 1, + ACTION_JULES_HOVER = 2, + ACTION_JULES_FLIGHT = 3, + ACTION_JULES_MOVE_HOME = 4, +}; + +enum ExorcismEvents +{ + EVENT_BARADAS_TALK = 1, + + //Colonel Jules + EVENT_SUMMON_SKULL = 1, +}; + +/*###### +## npc_barada +######*/ + +class npc_barada : public CreatureScript +{ +public: + npc_barada() : CreatureScript("npc_barada") { } + + struct npc_baradaAI : public ScriptedAI + { + npc_baradaAI(Creature* creature) : ScriptedAI(creature) + { + Initialize(); + } + + void Initialize() + { + step = 0; + } + + void Reset() override + { + events.Reset(); + Initialize(); + + playerGUID.Clear(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED); + } + + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override + { + player->PlayerTalkClass->ClearMenus(); + switch (gossipListId) + { + case 1: + player->PlayerTalkClass->SendCloseGossip(); + me->AI()->Talk(SAY_BARADA_1); + me->AI()->DoAction(ACTION_START_EVENT); + break; + default: + break; + } + } + + void DoAction(int32 action) override + { + if (action == ACTION_START_EVENT) + { + if (Creature* jules = me->FindNearestCreature(NPC_COLONEL_JULES, 20.0f, true)) + { + julesGUID = jules->GetGUID(); + jules->AI()->Talk(SAY_JULES_1); + } + + me->GetMotionMaster()->MovePoint(0, exorcismPos[1]); + Talk(SAY_BARADA_2); + + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED); + } + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) + return; + + if (id == 0) + me->GetMotionMaster()->MovePoint(1, exorcismPos[1]); + + if (id == 1) + events.ScheduleEvent(EVENT_BARADAS_TALK, 2000); + } + + void JustDied(Unit* /*killer*/) override + { + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + { + jules->AI()->DoAction(ACTION_JULES_MOVE_HOME); + jules->RemoveAllAuras(); + } + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BARADAS_TALK: + switch (step) + { + case 0: + me->SetFacingTo(1.513286f); + + me->HandleEmoteCommand(EMOTE_ONESHOT_KNEEL); + events.ScheduleEvent(EVENT_BARADAS_TALK, 3000); + step++; + break; + case 1: + DoCast(SPELL_BARADAS_COMMAND); + events.ScheduleEvent(EVENT_BARADAS_TALK, 5000); + step++; + break; + case 2: + Talk(SAY_BARADA_3); + events.ScheduleEvent(EVENT_BARADAS_TALK, 7000); + step++; + break; + case 3: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->AI()->Talk(SAY_JULES_2); + + events.ScheduleEvent(EVENT_BARADAS_TALK, 18000); + step++; + break; + case 4: + DoCast(SPELL_BARADA_FALTERS); + me->HandleEmoteCommand(EMOTE_STAND_STATE_NONE); + + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->AI()->DoAction(ACTION_JULES_HOVER); + + events.ScheduleEvent(EVENT_BARADAS_TALK, 11000); + step++; + break; + case 5: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->AI()->Talk(SAY_JULES_3); + + events.ScheduleEvent(EVENT_BARADAS_TALK, 13000); + step++; + break; + case 6: + Talk(SAY_BARADA_4); + events.ScheduleEvent(EVENT_BARADAS_TALK, 5000); + step++; + break; + case 7: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->AI()->Talk(SAY_JULES_3); + + events.ScheduleEvent(EVENT_BARADAS_TALK, 13000); + step++; + break; + case 8: + Talk(SAY_BARADA_4); + events.ScheduleEvent(EVENT_BARADAS_TALK, 12000); + step++; + break; + case 9: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->AI()->Talk(SAY_JULES_4); + + events.ScheduleEvent(EVENT_BARADAS_TALK, 12000); + step++; + break; + case 10: + Talk(SAY_BARADA_4); + events.ScheduleEvent(EVENT_BARADAS_TALK, 5000); + step++; + break; + case 11: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->AI()->DoAction(ACTION_JULES_FLIGHT); + + events.ScheduleEvent(EVENT_BARADAS_TALK, 10000); + step++; + break; + case 12: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->AI()->Talk(SAY_JULES_4); + + events.ScheduleEvent(EVENT_BARADAS_TALK, 8000); + step++; + break; + case 13: + Talk(SAY_BARADA_5); + events.ScheduleEvent(EVENT_BARADAS_TALK, 10000); + step++; + break; + case 14: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->AI()->Talk(SAY_JULES_4); + + events.ScheduleEvent(EVENT_BARADAS_TALK, 10000); + step++; + break; + case 15: + Talk(SAY_BARADA_6); + events.ScheduleEvent(EVENT_BARADAS_TALK, 10000); + step++; + break; + case 16: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->AI()->Talk(SAY_JULES_5); + + events.ScheduleEvent(EVENT_BARADAS_TALK, 10000); + step++; + break; + case 17: + Talk(SAY_BARADA_7); + events.ScheduleEvent(EVENT_BARADAS_TALK, 10000); + step++; + break; + case 18: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->AI()->Talk(SAY_JULES_3); + + events.ScheduleEvent(EVENT_BARADAS_TALK, 10000); + step++; + break; + case 19: + Talk(SAY_BARADA_7); + events.ScheduleEvent(EVENT_BARADAS_TALK, 10000); + step++; + break; + case 20: + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + { + jules->AI()->DoAction(ACTION_JULES_MOVE_HOME); + jules->RemoveAura(SPELL_JULES_VOMITS_AURA); + } + + events.ScheduleEvent(EVENT_BARADAS_TALK, 10000); + step++; + break; + case 21: + //End + if (Player* player = ObjectAccessor::FindPlayer(playerGUID)) + player->KilledMonsterCredit(NPC_COLONEL_JULES, ObjectGuid::Empty); + + if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID)) + jules->RemoveAllAuras(); + + me->RemoveAura(SPELL_BARADAS_COMMAND); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED); + + Talk(SAY_BARADA_8); + me->GetMotionMaster()->MoveTargetedHome(); + EnterEvadeMode(); + break; + } + break; + } + } + } + + private: + EventMap events; + uint8 step; + ObjectGuid julesGUID; + ObjectGuid playerGUID; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_baradaAI(creature); + } +}; + +/*###### +## npc_colonel_jules +######*/ + +class npc_colonel_jules : public CreatureScript +{ +public: + npc_colonel_jules() : CreatureScript("npc_colonel_jules") { } + + struct npc_colonel_julesAI : public ScriptedAI + { + npc_colonel_julesAI(Creature* creature) : ScriptedAI(creature), summons(me) + { + Initialize(); + } + + void Initialize() + { + circleRounds = 0; + point = 0; + } + + void Reset() override + { + events.Reset(); + + summons.DespawnAll(); + circleRounds = 0; + point = 3; + wpreached = false; + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_JULES_HOVER: + me->AddAura(SPELL_JULES_GOES_PRONE, me); + me->AddAura(SPELL_JULES_THREATENS_AURA, me); + + me->SetCanFly(true); + me->SetSpeed(MOVE_RUN, 0.2f); + + me->SetFacingTo(3.207566f); + me->GetMotionMaster()->MoveJump(exorcismPos[2], 2.0f, 2.0f); + + events.ScheduleEvent(EVENT_SUMMON_SKULL, 10000); + break; + case ACTION_JULES_FLIGHT: + circleRounds++; + + me->RemoveAura(SPELL_JULES_GOES_PRONE); + + me->AddAura(SPELL_JULES_GOES_UPRIGHT, me); + me->AddAura(SPELL_JULES_VOMITS_AURA, me); + + wpreached = true; + me->GetMotionMaster()->MovePoint(point, exorcismPos[point]); + break; + case ACTION_JULES_MOVE_HOME: + wpreached = false; + me->SetSpeed(MOVE_RUN, 1.0f); + me->GetMotionMaster()->MovePoint(11, exorcismPos[2]); + + events.CancelEvent(EVENT_SUMMON_SKULL); + break; + } + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + summon->GetMotionMaster()->MoveRandom(10.0f); + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) + return; + + if (id < 10) + wpreached = true; + + if (id == 8) + { + for (uint8 i = 0; i < circleRounds; i++) + DoSummon(NPC_FOUL_PURGE, exorcismPos[8]); + } + + if (id == 10) + { + wpreached = true; + point = 3; + circleRounds++; + } + } + + void UpdateAI(uint32 diff) override + { + if (wpreached) + { + me->GetMotionMaster()->MovePoint(point, exorcismPos[point]); + point++; + wpreached = false; + } + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SUMMON_SKULL: + uint8 summonCount = urand(1,3); + + for (uint8 i = 0; i < summonCount; i++) + me->SummonCreature(NPC_DARKNESS_RELEASED, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 1.5f, 0, TEMPSUMMON_MANUAL_DESPAWN); + + events.ScheduleEvent(EVENT_SUMMON_SKULL, urand(10000, 15000)); + break; + } + } + } + + private: + EventMap events; + SummonList summons; + + uint8 circleRounds; + uint8 point; + + bool wpreached; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_colonel_julesAI(creature); + } +}; + void AddSC_hellfire_peninsula() { new npc_aeranas(); new npc_ancestral_wolf(); new npc_wounded_blood_elf(); new npc_fel_guard_hound(); + new npc_barada(); + new npc_colonel_jules(); } diff --git a/src/server/scripts/World/duel_reset.cpp b/src/server/scripts/World/duel_reset.cpp new file mode 100644 index 00000000000..f08469d5bd5 --- /dev/null +++ b/src/server/scripts/World/duel_reset.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ScriptMgr.h" +#include "Player.h" + +class DuelResetScript : public PlayerScript +{ + public: + DuelResetScript() : PlayerScript("DuelResetScript") { } + + // Called when a duel starts (after 3s countdown) + void OnDuelStart(Player* player1, Player* player2) override + { + if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS)) + { + player1->GetSpellHistory()->SaveCooldownStateBeforeDuel(); + player2->GetSpellHistory()->SaveCooldownStateBeforeDuel(); + + player1->RemoveArenaSpellCooldowns(true); + player2->RemoveArenaSpellCooldowns(true); + } + } + + // Called when a duel ends + void OnDuelEnd(Player* winner, Player* loser, DuelCompleteType /*type*/) override + { + if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS)) + { + winner->RemoveArenaSpellCooldowns(true); + loser->RemoveArenaSpellCooldowns(true); + + winner->GetSpellHistory()->RestoreCooldownStateAfterDuel(); + loser->GetSpellHistory()->RestoreCooldownStateAfterDuel(); + } + } +}; + +void AddSC_duel_reset() +{ + new DuelResetScript(); +} + diff --git a/src/server/scripts/World/npc_professions.cpp b/src/server/scripts/World/npc_professions.cpp index e2ab2860796..a9261849f38 100644 --- a/src/server/scripts/World/npc_professions.cpp +++ b/src/server/scripts/World/npc_professions.cpp @@ -179,6 +179,52 @@ enum ProfessionSpells }; /*### +# specialization trainers +###*/ +enum SpecializationTrainers +{ + /* Alchemy */ + N_TRAINER_TRANSMUTE = 22427, // Zarevhi + N_TRAINER_ELIXIR = 19052, // Lorokeem + N_TRAINER_POTION = 17909, // Lauranna Thar'well + + /* Blacksmithing */ + N_TRAINER_SMITHOMNI1 = 11145, // Myolor Sunderfury + N_TRAINER_SMITHOMNI2 = 11176, // Krathok Moltenfist + N_TRAINER_WEAPON1 = 11146, // Ironus Coldsteel + N_TRAINER_WEAPON2 = 11178, // Borgosh Corebender + N_TRAINER_ARMOR1 = 5164, // Grumnus Steelshaper + N_TRAINER_ARMOR2 = 11177, // Okothos Ironrager + N_TRAINER_HAMMER = 11191, // Lilith the Lithe + N_TRAINER_AXE = 11192, // Kilram + N_TRAINER_SWORD = 11193, // Seril Scourgebane + + /* Leatherworking */ + N_TRAINER_DRAGON1 = 7866, // Peter Galen + N_TRAINER_DRAGON2 = 7867, // Thorkaf Dragoneye + N_TRAINER_ELEMENTAL1 = 7868, // Sarah Tanner + N_TRAINER_ELEMENTAL2 = 7869, // Brumn Winterhoof + N_TRAINER_TRIBAL1 = 7870, // Caryssia Moonhunter + N_TRAINER_TRIBAL2 = 7871, // Se'Jib + + /* Tailoring */ + N_TRAINER_SPELLFIRE = 22213, // Gidge Spellweaver + N_TRAINER_MOONCLOTH = 22208, // Nasmara Moonsong + N_TRAINER_SHADOWEAVE = 22212, // Andrion Darkspinner +}; + +/*### +# specialization quests +###*/ +enum SpecializationQuests +{ + /* Alchemy */ + Q_MASTER_TRANSMUTE = 10899, + Q_MASTER_ELIXIR = 10902, + Q_MASTER_POTION = 10897, +}; + +/*### # formulas to calculate unlearning cost ###*/ @@ -398,23 +444,23 @@ public: if (player->HasSkill(SKILL_ALCHEMY) && player->GetBaseSkillValue(SKILL_ALCHEMY) >= 350 && player->getLevel() > 67) { - if (player->GetQuestRewardStatus(10899) || player->GetQuestRewardStatus(10902) || player->GetQuestRewardStatus(10897)) + if (player->GetQuestRewardStatus(Q_MASTER_TRANSMUTE) || player->GetQuestRewardStatus(Q_MASTER_ELIXIR) || player->GetQuestRewardStatus(Q_MASTER_POTION)) { switch (creature->GetEntry()) { - case 22427: //Zarevhi + case N_TRAINER_TRANSMUTE: //Zarevhi if (!HasAlchemySpell(player)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_TRANSMUTE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 1); if (player->HasSpell(S_TRANSMUTE)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_TRANSMUTE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 4); break; - case 19052: //Lorokeem + case N_TRAINER_ELIXIR: //Lorokeem if (!HasAlchemySpell(player)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_ELIXIR, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 2); if (player->HasSpell(S_ELIXIR)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_ELIXIR, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 5); break; - case 17909: //Lauranna Thar'well + case N_TRAINER_POTION: //Lauranna Thar'well if (!HasAlchemySpell(player)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_POTION, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 3); if (player->HasSpell(S_POTION)) @@ -467,17 +513,17 @@ public: { switch (creature->GetEntry()) { - case 22427: + case N_TRAINER_TRANSMUTE: player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_TRANSMUTE, GOSSIP_SENDER_CHECK, action); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 19052: + case N_TRAINER_ELIXIR: player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_ELIXIR, GOSSIP_SENDER_CHECK, action); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 17909: + case N_TRAINER_POTION: player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_POTION, GOSSIP_SENDER_CHECK, action); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); @@ -492,17 +538,17 @@ public: { switch (creature->GetEntry()) { - case 22427: //Zarevhi + case N_TRAINER_TRANSMUTE: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_TRANSMUTE, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ALCHEMY_SPEC, DoHighUnlearnCost(player), false); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 19052: //Lorokeem + case N_TRAINER_ELIXIR: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_ELIXIR, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ALCHEMY_SPEC, DoHighUnlearnCost(player), false); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 17909: //Lauranna Thar'well + case N_TRAINER_POTION: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_POTION, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ALCHEMY_SPEC, DoHighUnlearnCost(player), false); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); @@ -567,20 +613,20 @@ public: { switch (creatureId) { - case 11145: //Myolor Sunderfury - case 11176: //Krathok Moltenfist + case N_TRAINER_SMITHOMNI1: + case N_TRAINER_SMITHOMNI2: if (!player->HasSpell(S_ARMOR) && !player->HasSpell(S_WEAPON) && player->GetReputationRank(REP_ARMOR) >= REP_FRIENDLY) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ARMOR_LEARN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); if (!player->HasSpell(S_WEAPON) && !player->HasSpell(S_ARMOR) && player->GetReputationRank(REP_WEAPON) >= REP_FRIENDLY) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_WEAPON_LEARN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); break; - case 11146: //Ironus Coldsteel - case 11178: //Borgosh Corebender + case N_TRAINER_WEAPON1: + case N_TRAINER_WEAPON2: if (player->HasSpell(S_WEAPON)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_WEAPON_UNLEARN, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 3); break; - case 5164: //Grumnus Steelshaper - case 11177: //Okothos Ironrager + case N_TRAINER_ARMOR1: + case N_TRAINER_ARMOR2: if (player->HasSpell(S_ARMOR)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ARMOR_UNLEARN, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 4); break; @@ -591,19 +637,19 @@ public: { switch (creatureId) { - case 11191: //Lilith the Lithe + case N_TRAINER_HAMMER: if (!HasWeaponSub(player)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_HAMMER, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 5); if (player->HasSpell(S_HAMMER)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_HAMMER, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 8); break; - case 11192: //Kilram + case N_TRAINER_AXE: if (!HasWeaponSub(player)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_AXE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 6); if (player->HasSpell(S_AXE)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_AXE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 9); break; - case 11193: //Seril Scourgebane + case N_TRAINER_SWORD: if (!HasWeaponSub(player)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SWORD, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 7); if (player->HasSpell(S_SWORD)) @@ -688,17 +734,17 @@ public: { switch (creature->GetEntry()) { - case 11191: + case N_TRAINER_HAMMER: player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_HAMMER, GOSSIP_SENDER_CHECK, action); //unknown textID (TALK_HAMMER_LEARN) player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 11192: + case N_TRAINER_AXE: player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_AXE, GOSSIP_SENDER_CHECK, action); //unknown textID (TALK_AXE_LEARN) player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 11193: + case N_TRAINER_SWORD: player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SWORD, GOSSIP_SENDER_CHECK, action); //unknown textID (TALK_SWORD_LEARN) player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); @@ -713,26 +759,26 @@ public: { switch (creature->GetEntry()) { - case 11146: //Ironus Coldsteel - case 11178: //Borgosh Corebender - case 5164: //Grumnus Steelshaper - case 11177: //Okothos Ironrager + case N_TRAINER_WEAPON1: + case N_TRAINER_WEAPON2: + case N_TRAINER_ARMOR1: + case N_TRAINER_ARMOR2: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_SMITH_SPEC, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ARMORORWEAPON, DoLowUnlearnCost(player), false); //unknown textID (TALK_UNLEARN_AXEORWEAPON) player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 11191: + case N_TRAINER_HAMMER: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_HAMMER, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_WEAPON_SPEC, DoMedUnlearnCost(player), false); //unknown textID (TALK_HAMMER_UNLEARN) player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 11192: + case N_TRAINER_AXE: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_AXE, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_WEAPON_SPEC, DoMedUnlearnCost(player), false); //unknown textID (TALK_AXE_UNLEARN) player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 11193: + case N_TRAINER_SWORD: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_SWORD, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_WEAPON_SPEC, DoMedUnlearnCost(player), false); //unknown textID (TALK_SWORD_UNLEARN) player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); @@ -904,18 +950,18 @@ public: { switch (creature->GetEntry()) { - case 7866: //Peter Galen - case 7867: //Thorkaf Dragoneye + case N_TRAINER_DRAGON1: + case N_TRAINER_DRAGON2: if (player->HasSpell(S_DRAGON)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_DRAGON, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 1); break; - case 7868: //Sarah Tanner - case 7869: //Brumn Winterhoof + case N_TRAINER_ELEMENTAL1: + case N_TRAINER_ELEMENTAL2: if (player->HasSpell(S_ELEMENTAL)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_ELEMENTAL, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 2); break; - case 7870: //Caryssia Moonhunter - case 7871: //Se'Jib + case N_TRAINER_TRIBAL1: + case N_TRAINER_TRIBAL2: if (player->HasSpell(S_TRIBAL)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_TRIBAL, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 3); break; @@ -955,20 +1001,20 @@ public: { switch (creature->GetEntry()) { - case 7866: //Peter Galen - case 7867: //Thorkaf Dragoneye + case N_TRAINER_DRAGON1: + case N_TRAINER_DRAGON2: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_DRAGON, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_LEATHER_SPEC, DoMedUnlearnCost(player), false); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 7868: //Sarah Tanner - case 7869: //Brumn Winterhoof + case N_TRAINER_ELEMENTAL1: + case N_TRAINER_ELEMENTAL2: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_ELEMENTAL, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_LEATHER_SPEC, DoMedUnlearnCost(player), false); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 7870: //Caryssia Moonhunter - case 7871: //Se'Jib + case N_TRAINER_TRIBAL1: + case N_TRAINER_TRIBAL2: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_TRIBAL, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_LEATHER_SPEC, DoMedUnlearnCost(player), false); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); @@ -1030,19 +1076,19 @@ public: { switch (creature->GetEntry()) { - case 22213: //Gidge Spellweaver + case N_TRAINER_SPELLFIRE: if (!HasTailorSpell(player)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SPELLFIRE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 1); if (player->HasSpell(S_SPELLFIRE)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_SPELLFIRE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 4); break; - case 22208: //Nasmara Moonsong + case N_TRAINER_MOONCLOTH: if (!HasTailorSpell(player)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_MOONCLOTH, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 2); if (player->HasSpell(S_MOONCLOTH)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_MOONCLOTH, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 5); break; - case 22212: //Andrion Darkspinner + case N_TRAINER_SHADOWEAVE: if (!HasTailorSpell(player)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SHADOWEAVE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 3); if (player->HasSpell(S_SHADOWEAVE)) @@ -1095,17 +1141,17 @@ public: { switch (creature->GetEntry()) { - case 22213: + case N_TRAINER_SPELLFIRE: player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SPELLFIRE, GOSSIP_SENDER_CHECK, action); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 22208: + case N_TRAINER_MOONCLOTH: player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_MOONCLOTH, GOSSIP_SENDER_CHECK, action); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 22212: + case N_TRAINER_SHADOWEAVE: player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LEARN_SHADOWEAVE, GOSSIP_SENDER_CHECK, action); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); @@ -1120,17 +1166,17 @@ public: { switch (creature->GetEntry()) { - case 22213: //Gidge Spellweaver + case N_TRAINER_SPELLFIRE: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_SPELLFIRE, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_TAILOR_SPEC, DoHighUnlearnCost(player), false); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 22208: //Nasmara Moonsong + case N_TRAINER_MOONCLOTH: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_MOONCLOTH, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_TAILOR_SPEC, DoHighUnlearnCost(player), false); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); break; - case 22212: //Andrion Darkspinner + case N_TRAINER_SHADOWEAVE: player->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_SHADOWEAVE, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_TAILOR_SPEC, DoHighUnlearnCost(player), false); //unknown textID () player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 65daf1480ca..6120107a6f2 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -2250,6 +2250,7 @@ Battleground.StoreStatistics.Enable = 0 # Don't bother with balance) # 1 - (Experimental, Don't allow to invite much more players # of one faction) +# 2 - (Experimental, Try to have even teams) Battleground.InvitationType = 0 @@ -2659,6 +2660,14 @@ PlayerStart.MapsExplored = 0 HonorPointsAfterDuel = 0 # +# ResetDuelCooldowns +# Description: Reset all cooldowns before duel starts and restore them when duel ends. +# Default: 0 - (Disabled) +# 1 - (Enabled) + +ResetDuelCooldowns = 0 + +# # AlwaysMaxWeaponSkill # Description: Players will automatically gain max weapon/defense skill when logging in, # or leveling. |