diff options
Diffstat (limited to 'src')
108 files changed, 3547 insertions, 1823 deletions
diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index c748e9d28a8..eb3cf4c9964 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -46,25 +46,52 @@ using namespace boost::program_options; # define _TRINITY_REALM_CONFIG "authserver.conf" #endif +#if PLATFORM == PLATFORM_WINDOWS +#include "ServiceWin32.h" +char serviceName[] = "authserver"; +char serviceLongName[] = "TrinityCore auth service"; +char serviceDescription[] = "TrinityCore World of Warcraft emulator auth service"; +/* +* -1 - not in service mode +* 0 - stopped +* 1 - running +* 2 - paused +*/ +int m_ServiceStatus = -1; + +boost::asio::deadline_timer* _serviceStatusWatchTimer; +void ServiceStatusWatcher(boost::system::error_code const& error); +#endif + bool StartDB(); void StopDB(); void SignalHandler(const boost::system::error_code& error, int signalNumber); void KeepDatabaseAliveHandler(const boost::system::error_code& error); -variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile); +variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService); -boost::asio::io_service _ioService; -boost::asio::deadline_timer _dbPingTimer(_ioService); +boost::asio::io_service* _ioService; +boost::asio::deadline_timer* _dbPingTimer; uint32 _dbPingInterval; LoginDatabaseWorkerPool LoginDatabase; int main(int argc, char** argv) { std::string configFile = _TRINITY_REALM_CONFIG; - auto vm = GetConsoleArguments(argc, argv, configFile); + std::string configService; + auto vm = GetConsoleArguments(argc, argv, configFile, configService); // exit if help is enabled if (vm.count("help")) return 0; +#if PLATFORM == PLATFORM_WINDOWS + if (configService.compare("install") == 0) + return WinServiceInstall() == true ? 0 : 1; + else if (configService.compare("uninstall") == 0) + return WinServiceUninstall() == true ? 0 : 1; + else if (configService.compare("run") == 0) + return WinServiceRun() ? 0 : 1; +#endif + std::string configError; if (!sConfigMgr->LoadInitial(configFile, configError)) { @@ -95,13 +122,16 @@ int main(int argc, char** argv) if (!StartDB()) return 1; + _ioService = new boost::asio::io_service(); + // Get the list of realms for the server - sRealmList->Initialize(_ioService, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 20)); + sRealmList->Initialize(*_ioService, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 20)); if (sRealmList->size() == 0) { TC_LOG_ERROR("server.authserver", "No valid realms specified."); StopDB(); + delete _ioService; return 1; } @@ -111,15 +141,16 @@ int main(int argc, char** argv) { TC_LOG_ERROR("server.authserver", "Specified port out of allowed range (1-65535)"); StopDB(); + delete _ioService; return 1; } std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); - sAuthSocketMgr.StartNetwork(_ioService, bindIp, port); + sAuthSocketMgr.StartNetwork(*_ioService, bindIp, port); // Set signal handlers - boost::asio::signal_set signals(_ioService, SIGINT, SIGTERM); + boost::asio::signal_set signals(*_ioService, SIGINT, SIGTERM); #if PLATFORM == PLATFORM_WINDOWS signals.add(SIGBREAK); #endif @@ -130,20 +161,38 @@ int main(int argc, char** argv) // Enabled a timed callback for handling the database keep alive ping _dbPingInterval = sConfigMgr->GetIntDefault("MaxPingTime", 30); - _dbPingTimer.expires_from_now(boost::posix_time::minutes(_dbPingInterval)); - _dbPingTimer.async_wait(KeepDatabaseAliveHandler); + _dbPingTimer = new boost::asio::deadline_timer(*_ioService); + _dbPingTimer->expires_from_now(boost::posix_time::minutes(_dbPingInterval)); + _dbPingTimer->async_wait(KeepDatabaseAliveHandler); + +#if PLATFORM == PLATFORM_WINDOWS + if (m_ServiceStatus != -1) + { + _serviceStatusWatchTimer = new boost::asio::deadline_timer(*_ioService); + _serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1)); + _serviceStatusWatchTimer->async_wait(ServiceStatusWatcher); + } +#endif // Start the io service worker loop - _ioService.run(); + _ioService->run(); + + _dbPingTimer->cancel(); + + sAuthSocketMgr.StopNetwork(); // Close the Database Pool and library StopDB(); TC_LOG_INFO("server.authserver", "Halting process..."); + + signals.cancel(); + + delete _dbPingTimer; + delete _ioService; return 0; } - /// Initialize connection to the database bool StartDB() { @@ -174,7 +223,7 @@ void StopDB() void SignalHandler(const boost::system::error_code& error, int /*signalNumber*/) { if (!error) - _ioService.stop(); + _ioService->stop(); } void KeepDatabaseAliveHandler(const boost::system::error_code& error) @@ -184,31 +233,60 @@ void KeepDatabaseAliveHandler(const boost::system::error_code& error) TC_LOG_INFO("server.authserver", "Ping MySQL to keep connection alive"); LoginDatabase.KeepAlive(); - _dbPingTimer.expires_from_now(boost::posix_time::minutes(_dbPingInterval)); - _dbPingTimer.async_wait(KeepDatabaseAliveHandler); + _dbPingTimer->expires_from_now(boost::posix_time::minutes(_dbPingInterval)); + _dbPingTimer->async_wait(KeepDatabaseAliveHandler); } } -variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile) +#if PLATFORM == PLATFORM_WINDOWS +void ServiceStatusWatcher(boost::system::error_code const& error) +{ + if (!error) + { + if (m_ServiceStatus == 0) + { + _ioService->stop(); + delete _serviceStatusWatchTimer; + } + else + { + _serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1)); + _serviceStatusWatchTimer->async_wait(ServiceStatusWatcher); + } + } +} +#endif + +variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService) { options_description all("Allowed options"); all.add_options() ("help,h", "print usage message") ("config,c", value<std::string>(&configFile)->default_value(_TRINITY_REALM_CONFIG), "use <arg> as configuration file") ; +#if PLATFORM == PLATFORM_WINDOWS + options_description win("Windows platform specific options"); + win.add_options() + ("service,s", value<std::string>(&configService)->default_value(""), "Windows service options: [install | uninstall]") + ; + + all.add(win); +#else + (void)configService; +#endif variables_map variablesMap; try { store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), variablesMap); notify(variablesMap); } - catch (std::exception& e) { + catch (std::exception& e) + { std::cerr << e.what() << "\n"; } - if (variablesMap.count("help")) { + if (variablesMap.count("help")) std::cout << all << "\n"; - } return variablesMap; } diff --git a/src/server/collision/Models/GameObjectModel.cpp b/src/server/collision/Models/GameObjectModel.cpp index 0355d2e848c..dd1502c45b0 100644 --- a/src/server/collision/Models/GameObjectModel.cpp +++ b/src/server/collision/Models/GameObjectModel.cpp @@ -186,12 +186,12 @@ bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool Sto return hit; } -bool GameObjectModel::Relocate(const GameObject& go) +bool GameObjectModel::UpdatePosition() { if (!iModel) return false; - ModelList::const_iterator it = model_list.find(go.GetDisplayId()); + ModelList::const_iterator it = model_list.find(owner->GetDisplayId()); if (it == model_list.end()) return false; @@ -203,9 +203,9 @@ bool GameObjectModel::Relocate(const GameObject& go) return false; } - iPos = Vector3(go.GetPositionX(), go.GetPositionY(), go.GetPositionZ()); + iPos = Vector3(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ()); - G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(go.GetOrientation(), 0, 0); + G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(owner->GetOrientation(), 0, 0); iInvRot = iRotation.inverse(); // transform bounding box: mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale); @@ -219,7 +219,7 @@ bool GameObjectModel::Relocate(const GameObject& go) for (int i = 0; i < 8; ++i) { Vector3 pos(iBound.corner(i)); - go.SummonCreature(1, pos.x, pos.y, pos.z, 0, TEMPSUMMON_MANUAL_DESPAWN); + owner->SummonCreature(1, pos.x, pos.y, pos.z, 0, TEMPSUMMON_MANUAL_DESPAWN); } #endif diff --git a/src/server/collision/Models/GameObjectModel.h b/src/server/collision/Models/GameObjectModel.h index 232ebbee5e9..43d299d6d8f 100644 --- a/src/server/collision/Models/GameObjectModel.h +++ b/src/server/collision/Models/GameObjectModel.h @@ -68,7 +68,7 @@ public: static GameObjectModel* Create(const GameObject& go); - bool Relocate(GameObject const& go); + bool UpdatePosition(); }; #endif // _GAMEOBJECT_MODEL_H diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 9eafcc30c81..efe55830b1b 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -128,7 +128,7 @@ class UnitAI virtual void InitializeAI() { if (!me->isDead()) Reset(); } - virtual void Reset() { }; + virtual void Reset() { } // Called when unit is charmed virtual void OnCharmed(bool apply) = 0; diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 20a7ca05d93..21a15fa4f99 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -581,7 +581,11 @@ void SmartAI::JustDied(Unit* killer) { GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, killer); if (HasEscortState(SMART_ESCORT_ESCORTING)) + { EndPath(true); + me->StopMoving();//force stop + me->GetMotionMaster()->MoveIdle(); + } } void SmartAI::KilledUnit(Unit* victim) diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h index a3d75f1889b..51628eded54 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.h +++ b/src/server/game/AI/SmartScripts/SmartScript.h @@ -247,7 +247,14 @@ class SmartScript DecPhase(abs(p)); } - void DecPhase(int32 p = 1) { mEventPhase -= (mEventPhase < (uint32)p ? (uint32)p - mEventPhase : (uint32)p); } + void DecPhase(int32 p = 1) + { + if(mEventPhase > (uint32)p) + mEventPhase -= (uint32)p; + else + mEventPhase = 0; + } + bool IsInPhase(uint32 p) const { return ((1 << (mEventPhase - 1)) & p) != 0; } void SetPhase(uint32 p = 0) { mEventPhase = p; } diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 8f9db8d5a27..339788dee01 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -802,6 +802,7 @@ class RBACData * @return Success or failure (with reason) to grant the permission * * Example Usage: + * @code * // previously defined "RBACData* rbac" with proper initialization * uint32 permissionId = 2; * if (rbac->GrantRole(permissionId) == RBAC_IN_DENIED_LIST) @@ -825,6 +826,7 @@ class RBACData * @return Success or failure (with reason) to deny the permission * * Example Usage: + * @code * // previously defined "RBACData* rbac" with proper initialization * uint32 permissionId = 2; * if (rbac->DenyRole(permissionId) == RBAC_ID_DOES_NOT_EXISTS) @@ -849,6 +851,7 @@ class RBACData * @return Success or failure (with reason) to remove the permission * * Example Usage: + * @code * // previously defined "RBACData* rbac" with proper initialization * uint32 permissionId = 2; * if (rbac->RevokeRole(permissionId) == RBAC_OK) @@ -939,9 +942,6 @@ class RBACData * * Given a list of permissions, gets all the inherited permissions * @param permissions The list of permissions to expand - * - * @return new list of permissions containing original permissions and - * all other pemissions that are linked to the original ones */ void ExpandPermissions(RBACPermissionContainer& permissions); diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index 95a51f6d915..8e5766407f1 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -514,7 +514,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates() _battlegroundTemplates.clear(); // 0 1 2 3 4 5 6 7 8 9 10 11 - QueryResult result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, AllianceStartLoc, AllianceStartO, HordeStartLoc, HordeStartO, StartMaxDist, Weight, ScriptName FROM battleground_template"); + QueryResult result = WorldDatabase.Query("SELECT ID, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, AllianceStartLoc, AllianceStartO, HordeStartLoc, HordeStartO, StartMaxDist, Weight, ScriptName FROM battleground_template"); if (!result) { TC_LOG_ERROR("server.loading", ">> Loaded 0 battlegrounds. DB table `battleground_template` is empty."); diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp index d9db2a9b5d1..43b7acde925 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp +++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp @@ -889,7 +889,8 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 /*diff*/, BattlegroundTyp // (after what time the ratings aren't taken into account when making teams) then // the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account // else leave the discard time on 0, this way all ratings will be discarded - uint32 discardTime = getMSTime() - sBattlegroundMgr->GetRatingDiscardTimer(); + // this has to be signed value - when the server starts, this value would be negative and thus overflow + int32 discardTime = getMSTime() - sBattlegroundMgr->GetRatingDiscardTimer(); // we need to find 2 teams which will play next game GroupsQueueType::iterator itr_teams[BG_TEAMS_COUNT]; @@ -905,7 +906,7 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 /*diff*/, BattlegroundTyp // if group match conditions, then add it to pool if (!(*itr2)->IsInvitedToBGInstanceGUID && (((*itr2)->ArenaMatchmakerRating >= arenaMinRating && (*itr2)->ArenaMatchmakerRating <= arenaMaxRating) - || (*itr2)->JoinTime < discardTime)) + || (int32)(*itr2)->JoinTime < discardTime)) { itr_teams[found++] = itr2; team = i; @@ -923,7 +924,7 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 /*diff*/, BattlegroundTyp { if (!(*itr3)->IsInvitedToBGInstanceGUID && (((*itr3)->ArenaMatchmakerRating >= arenaMinRating && (*itr3)->ArenaMatchmakerRating <= arenaMaxRating) - || (*itr3)->JoinTime < discardTime) + || (int32)(*itr3)->JoinTime < discardTime) && (*itr_teams[0])->ArenaTeamId != (*itr3)->ArenaTeamId) { itr_teams[found++] = itr3; diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.h b/src/server/game/Battlegrounds/BattlegroundQueue.h index b0adaa4078d..7e8debfd24d 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.h +++ b/src/server/game/Battlegrounds/BattlegroundQueue.h @@ -106,7 +106,7 @@ class BattlegroundQueue class SelectionPool { public: - SelectionPool(): PlayerCount(0) { }; + SelectionPool(): PlayerCount(0) { } void Init(); bool AddGroup(GroupQueueInfo* ginfo, uint32 desiredCount); bool KickGroup(uint32 size); diff --git a/src/server/game/Chat/Channels/Channel.h b/src/server/game/Chat/Channels/Channel.h index 9b9c2f1e514..0f2a940e3b4 100644 --- a/src/server/game/Chat/Channels/Channel.h +++ b/src/server/game/Chat/Channels/Channel.h @@ -184,7 +184,7 @@ class Channel void DeVoice(ObjectGuid guid1, ObjectGuid guid2); void JoinNotify(ObjectGuid guid); // invisible notify void LeaveNotify(ObjectGuid guid); // invisible notify - void SetOwnership(bool ownership) { _ownership = ownership; }; + void SetOwnership(bool ownership) { _ownership = ownership; } static void CleanOldChannelsInDB(); private: diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 1716aa73525..3cbdcbcaff1 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -78,6 +78,7 @@ ChatCommand* ChatHandler::getCommandTable() // cache top-level commands size_t added = 0; + free(commandTableCache); commandTableCache = (ChatCommand*)malloc(sizeof(ChatCommand) * total); ASSERT(commandTableCache); memset(commandTableCache, 0, sizeof(ChatCommand) * total); diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 25fbef2ca86..b05f9ac2a0d 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1535,6 +1535,9 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) if (!((1 << i) & cond->SourceGroup)) continue; + if (spellInfo->Effects[i].ChainTarget > 0) + continue; + switch (spellInfo->Effects[i].TargetA.GetSelectionCategory()) { case TARGET_SELECT_CATEGORY_NEARBY: @@ -1555,7 +1558,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) break; } - TC_LOG_ERROR("sql.sql", "SourceEntry %u SourceGroup %u in `condition` table - spell %u does not have implicit targets of types: _AREA_, _CONE_, _NEARBY_ for effect %u, SourceGroup needs correction, ignoring.", cond->SourceEntry, origGroup, cond->SourceEntry, uint32(i)); + TC_LOG_ERROR("sql.sql", "SourceEntry %u SourceGroup %u in `condition` table - spell %u does not have implicit targets of types: _AREA_, _CONE_, _NEARBY_, __CHAIN__ for effect %u, SourceGroup needs correction, ignoring.", cond->SourceEntry, origGroup, cond->SourceEntry, uint32(i)); cond->SourceGroup &= ~(1 << i); } // all effects were removed, no need to add the condition at all diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index f07e4e7113f..59eedaaffed 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -194,11 +194,12 @@ void LFGMgr::LoadLFGDungeons(bool reload /* = false */) } // Fill teleport locations from DB - QueryResult result = WorldDatabase.Query("SELECT dungeonId, position_x, position_y, position_z, orientation FROM lfg_entrances"); + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT dungeonId, position_x, position_y, position_z, orientation FROM lfg_dungeon_template"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 lfg entrance positions. DB table `lfg_entrances` is empty!"); + TC_LOG_ERROR("server.loading", ">> Loaded 0 lfg entrance positions. DB table `lfg_dungeon_template` is empty!"); return; } @@ -211,7 +212,7 @@ void LFGMgr::LoadLFGDungeons(bool reload /* = false */) LFGDungeonContainer::iterator dungeonItr = LfgDungeonStore.find(dungeonId); if (dungeonItr == LfgDungeonStore.end()) { - TC_LOG_ERROR("sql.sql", "table `lfg_entrances` contains coordinates for wrong dungeon %u", dungeonId); + TC_LOG_ERROR("sql.sql", "table `lfg_dungeon_template` contains coordinates for wrong dungeon %u", dungeonId); continue; } @@ -410,6 +411,8 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const joinData.result = LFG_JOIN_RANDOM_COOLDOWN; else if (dungeons.empty()) joinData.result = LFG_JOIN_NOT_MEET_REQS; + else if (player->HasAura(9454)) // check Freeze debuff + joinData.result = LFG_JOIN_NOT_MEET_REQS; else if (grp) { if (grp->GetMembersCount() > MAXGROUPSIZE) @@ -429,6 +432,8 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const joinData.result = LFG_JOIN_PARTY_RANDOM_COOLDOWN; else if (plrg->InBattleground() || plrg->InArena() || plrg->InBattlegroundQueue()) joinData.result = LFG_JOIN_USING_BG_SYSTEM; + else if (plrg->HasAura(9454)) // check Freeze debuff + joinData.result = LFG_JOIN_PARTY_NOT_MEET_REQS; ++memberCount; players.insert(plrg->GetGUID()); } @@ -1281,6 +1286,8 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false* error = LFG_TELEPORTERROR_IN_VEHICLE; else if (player->GetCharmGUID()) error = LFG_TELEPORTERROR_CHARMING; + else if (player->HasAura(9454)) // check Freeze debuff + error = LFG_TELEPORTERROR_INVALID_LOCATION; else if (player->GetMapId() != uint32(dungeon->map)) // Do not teleport players in dungeon to the entrance { uint32 mapid = dungeon->map; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 7f00fc7f9d8..6e51257eb82 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1370,9 +1370,12 @@ bool Creature::CanStartAttack(Unit const* who, bool force) const if (IsCivilian()) return false; - if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC)) + // This set of checks is should be done only for creatures + if ((HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC) && who->GetTypeId() != TYPEID_PLAYER) // flag is valid only for non player characters + || (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) && who->GetTypeId() == TYPEID_PLAYER) // immune to PC and target is a player, return false + || (who->GetOwner() && who->GetOwner()->GetTypeId() == TYPEID_PLAYER && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC))) // player pets are immune to pc as well return false; - + // Do not attack non-combat pets if (who->GetTypeId() == TYPEID_UNIT && who->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET) return false; @@ -1510,11 +1513,11 @@ void Creature::setDeathState(DeathState s) SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag); ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_IGNORE_PATHFINDING)); SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); - LoadCreaturesAddon(true); Motion_Initialize(); if (GetCreatureData() && GetPhaseMask() != GetCreatureData()->phaseMask) SetPhaseMask(GetCreatureData()->phaseMask, false); Unit::setDeathState(ALIVE); + LoadCreaturesAddon(true); } } @@ -2415,7 +2418,7 @@ bool Creature::SetSwim(bool enable) return true; } -bool Creature::SetCanFly(bool enable) +bool Creature::SetCanFly(bool enable, bool /*packetOnly = false */) { if (!Unit::SetCanFly(enable)) return false; @@ -2638,3 +2641,29 @@ void Creature::StartPickPocketRefillTimer() _pickpocketLootRestore = time(NULL) + sWorld->getIntConfig(CONFIG_CREATURE_PICKPOCKET_REFILL); } +void Creature::SetTextRepeatId(uint8 textGroup, uint8 id) +{ + CreatureTextRepeatIds& repeats = m_textRepeat[textGroup]; + if (std::find(repeats.begin(), repeats.end(), id) == repeats.end()) + repeats.push_back(id); + else + TC_LOG_ERROR("sql.sql", "CreatureTextMgr: TextGroup %u for Creature(%s) GuidLow %u Entry %u, id %u already added", uint32(textGroup), GetName().c_str(), GetGUIDLow(), GetEntry(), uint32(id)); +} + +CreatureTextRepeatIds Creature::GetTextRepeatGroup(uint8 textGroup) +{ + CreatureTextRepeatIds ids; + + CreatureTextRepeatGroup::const_iterator groupItr = m_textRepeat.find(textGroup); + if (groupItr != m_textRepeat.end()) + ids = groupItr->second; + + return ids; +} + +void Creature::ClearTextRepeatGroup(uint8 textGroup) +{ + CreatureTextRepeatGroup::iterator groupItr = m_textRepeat.find(textGroup); + if (groupItr != m_textRepeat.end()) + groupItr->second.clear(); +} diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 6f82d5dda4a..0ad76ab2086 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -130,7 +130,6 @@ struct CreatureTemplate float ModDamage; float ModExperience; bool RacialLeader; - uint32 questItems[MAX_CREATURE_QUEST_ITEMS]; uint32 movementId; bool RegenHealth; uint32 MechanicImmuneMask; @@ -167,6 +166,9 @@ struct CreatureTemplate } }; +typedef std::vector<uint32> CreatureQuestItemList; +typedef std::unordered_map<uint32, CreatureQuestItemList> CreatureQuestItemMap; + // Benchmarked: Faster than std::map (insert/find) typedef std::unordered_map<uint32, CreatureTemplate> CreatureTemplateContainer; @@ -409,6 +411,10 @@ struct TrainerSpellData #define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY +//used for handling non-repeatable random texts +typedef std::vector<uint8> CreatureTextRepeatIds; +typedef std::unordered_map<uint8, CreatureTextRepeatIds> CreatureTextRepeatGroup; + class Creature : public Unit, public GridObject<Creature>, public MapObject { public: @@ -472,7 +478,7 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject bool SetWalk(bool enable) override; bool SetDisableGravity(bool disable, bool packetOnly = false) override; bool SetSwim(bool enable) override; - bool SetCanFly(bool enable) override; + bool SetCanFly(bool enable, bool packetOnly = false) override; bool SetWaterWalking(bool enable, bool packetOnly = false) override; bool SetFeatherFall(bool enable, bool packetOnly = false) override; bool SetHover(bool enable, bool packetOnly = false) override; @@ -656,6 +662,10 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject void FocusTarget(Spell const* focusSpell, WorldObject const* target); void ReleaseFocus(Spell const* focusSpell); + CreatureTextRepeatIds GetTextRepeatGroup(uint8 textGroup); + void SetTextRepeatId(uint8 textGroup, uint8 id); + void ClearTextRepeatGroup(uint8 textGroup); + protected: bool CreateFromProto(uint32 guidlow, uint32 entry, CreatureData const* data = nullptr, uint32 vehId = 0); bool InitEntry(uint32 entry, CreatureData const* data = nullptr); @@ -720,6 +730,8 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject bool TriggerJustRespawned; Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing + + CreatureTextRepeatGroup m_textRepeat; }; class AssistDelayEvent : public BasicEvent diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index a4b140b9878..50981a163be 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -1757,6 +1757,9 @@ void GameObject::Use(Unit* user) return; } + if (Player* player = user->ToPlayer())
+ sOutdoorPvPMgr->HandleCustomSpell(player, spellId, this);
+
if (spellCaster) spellCaster->CastSpell(user, spellInfo, triggered); else @@ -2314,7 +2317,7 @@ void GameObject::UpdateModelPosition() if (GetMap()->ContainsGameObjectModel(*m_model)) { GetMap()->RemoveGameObjectModel(*m_model); - m_model->Relocate(*this); + m_model->UpdatePosition(); GetMap()->InsertGameObjectModel(*m_model); } } diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 5f1d2c793e6..d413d6911a5 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -45,7 +45,6 @@ struct GameObjectTemplate uint32 faction; uint32 flags; float size; - uint32 questItems[MAX_GAMEOBJECT_QUEST_ITEMS]; union // different GO types have different data field { //0 GAMEOBJECT_TYPE_DOOR @@ -620,6 +619,9 @@ struct GameObjectData bool dbData; }; +typedef std::vector<uint32> GameObjectQuestItemList; +typedef std::unordered_map<uint32, GameObjectQuestItemList> GameObjectQuestItemMap; + // For containers: [GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY -> ... // For bobber: GO_NOT_READY ->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED-><deleted> // For door(closed):[GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY(close) -> ... diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 030c2a25110..55d882c65d3 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -9616,13 +9616,9 @@ void Player::SendBattlefieldWorldStates() { if (BattlefieldWG* wg = (BattlefieldWG*)sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG)) { - if (wg->IsWarTime()) - SendUpdateWorldState(ClockWorldState[1], uint32(time(NULL))); - else // Time to next battle - { - uint32 timer = wg->GetTimer() / 1000; - SendUpdateWorldState(ClockWorldState[1], time(NULL) + timer); - } + SendUpdateWorldState(BATTLEFIELD_WG_WORLD_STATE_ACTIVE, wg->IsWarTime() ? 0 : 1); + uint32 timer = wg->IsWarTime() ? 0 : (wg->GetTimer() / 1000); // 0 - Time to next battle + SendUpdateWorldState(ClockWorldState[1], uint32(time(NULL) + timer)); } } } @@ -17052,8 +17048,8 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) //"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, " // 39 40 41 42 43 44 45 46 47 48 49 //"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, " - // 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 - //"health, power1, power2, power3, power4, power5, power6, power7, instance_id, speccount, activespec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels FROM characters WHERE guid = '%u'", guid); + // 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 + //"health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels FROM characters WHERE guid = '%u'", guid); PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM); if (!result) { @@ -17812,9 +17808,9 @@ void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff) TC_LOG_DEBUG("entities.player.loading", "Loading auras for player %u", GetGUIDLow()); /* 0 1 2 3 4 5 6 7 8 9 10 - QueryResult* result = CharacterDatabase.PQuery("SELECT caster_guid, spell, effect_mask, recalculate_mask, stackcount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, + QueryResult* result = CharacterDatabase.PQuery("SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, 11 12 13 - maxduration, remaintime, remaincharges FROM character_aura WHERE guid = '%u'", GetGUIDLow()); + maxDuration, remainTime, remainCharges FROM character_aura WHERE guid = '%u'", GetGUIDLow()); */ if (result) @@ -23657,6 +23653,9 @@ bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const if (player->GetMapId() != pRewardSource->GetMapId() || player->GetInstanceId() != pRewardSource->GetInstanceId()) return false; + if (player->GetMap()->IsDungeon()) + return true; + return pRewardSource->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE); } @@ -25621,7 +25620,7 @@ void Player::SetMap(Map* map) void Player::_LoadGlyphs(PreparedQueryResult result) { - // SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 from character_glyphs WHERE guid = '%u' + // SELECT talentGroup, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 from character_glyphs WHERE guid = '%u' if (!result) return; @@ -25668,7 +25667,7 @@ void Player::_SaveGlyphs(SQLTransaction& trans) void Player::_LoadTalents(PreparedQueryResult result) { - // SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid = '%u'", GUID_LOPART(m_guid)); + // SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, talentGroup FROM character_talent WHERE guid = '%u'", GUID_LOPART(m_guid)); if (result) { do @@ -26251,9 +26250,9 @@ bool Player::SetDisableGravity(bool disable, bool packetOnly /*= false*/) return true; } -bool Player::SetCanFly(bool apply) +bool Player::SetCanFly(bool apply, bool packetOnly /*= false*/) { - if (!Unit::SetCanFly(apply)) + if (!packetOnly && !Unit::SetCanFly(apply)) return false; WorldPacket data(apply ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, 12); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index af640e4c73c..98b9d8a3d07 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -346,9 +346,9 @@ struct Runes struct EnchantDuration { - EnchantDuration() : item(NULL), slot(MAX_ENCHANTMENT_SLOT), leftduration(0) { }; + EnchantDuration() : item(NULL), slot(MAX_ENCHANTMENT_SLOT), leftduration(0) { } EnchantDuration(Item* _item, EnchantmentSlot _slot, uint32 _leftduration) : item(_item), slot(_slot), - leftduration(_leftduration){ ASSERT(item); }; + leftduration(_leftduration){ ASSERT(item); } Item* item; EnchantmentSlot slot; @@ -1664,7 +1664,7 @@ class Player : public Unit, public GridObject<Player> void setResurrectRequestData(ObjectGuid guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana); void clearResurrectRequestData() { setResurrectRequestData(ObjectGuid::Empty, 0, 0.0f, 0.0f, 0.0f, 0, 0); } - bool isResurrectRequestedBy(ObjectGuid guid) const { return m_resurrectGUID == guid; } + bool isResurrectRequestedBy(ObjectGuid guid) const { return !m_resurrectGUID.IsEmpty() && m_resurrectGUID == guid; } bool isResurrectRequested() const { return !m_resurrectGUID.IsEmpty(); } void ResurrectUsingRequestData(); @@ -1839,7 +1839,7 @@ class Player : public Unit, public GridObject<Player> bool UpdatePosition(const Position &pos, bool teleport = false) { return UpdatePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); } void UpdateUnderwaterState(Map* m, float x, float y, float z) override; - void SendMessageToSet(WorldPacket* data, bool self) override {SendMessageToSetInRange(data, GetVisibilityRange(), self); };// overwrite Object::SendMessageToSet + void SendMessageToSet(WorldPacket* data, bool self) override {SendMessageToSetInRange(data, GetVisibilityRange(), self); }// overwrite Object::SendMessageToSet void SendMessageToSetInRange(WorldPacket* data, float fist, bool self) override;// overwrite Object::SendMessageToSetInRange void SendMessageToSetInRange(WorldPacket* data, float dist, bool self, bool own_team_only); void SendMessageToSet(WorldPacket* data, Player const* skipped_rcvr) override; @@ -2301,7 +2301,7 @@ class Player : public Unit, public GridObject<Player> void RemoveFromWhisperWhiteList(ObjectGuid guid) { WhisperList.remove(guid); } bool SetDisableGravity(bool disable, bool packetOnly /* = false */) override; - bool SetCanFly(bool apply) override; + bool SetCanFly(bool apply, bool packetOnly = false) override; bool SetWaterWalking(bool apply, bool packetOnly = false) override; bool SetFeatherFall(bool apply, bool packetOnly = false) override; bool SetHover(bool enable, bool packetOnly = false) override; diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 85dccddf186..6d98a4c78b2 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -226,7 +226,7 @@ void Transport::Update(uint32 diff) sScriptMgr->OnTransportUpdate(this, diff); } -void Transport::DelayedUpdate(uint32 diff) +void Transport::DelayedUpdate(uint32 /*diff*/) { if (GetKeyFrames().size() <= 1) return; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 219ee8b1002..17915e53cfb 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -1787,6 +1787,13 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe dmgInfo.AbsorbDamage(splitDamage); + // check if caster is immune to damage + if (caster->IsImmunedToDamage(schoolMask)) + { + victim->SendSpellMiss(caster, (*itr)->GetSpellInfo()->Id, SPELL_MISS_IMMUNE); + continue; + } + uint32 splitted = splitDamage; uint32 splitted_absorb = 0; DealDamageMods(caster, splitted, &splitted_absorb); @@ -1824,6 +1831,14 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe splitDamage = RoundToInterval(splitDamage, uint32(0), uint32(dmgInfo.GetDamage())); dmgInfo.AbsorbDamage(splitDamage); + + // check if caster is immune to damage + if (caster->IsImmunedToDamage(schoolMask)) + { + victim->SendSpellMiss(caster, (*itr)->GetSpellInfo()->Id, SPELL_MISS_IMMUNE); + continue; + } + uint32 split_absorb = 0; DealDamageMods(caster, splitDamage, &split_absorb); @@ -9949,11 +9964,15 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin DoneTotal += int32(DoneAdvertisedBenefit * coeff * factorMod); } - // Done Percentage for DOT is already calculated, no need to do it again. The percentage mod is applied in Aura::HandleAuraSpecificMods. - float tmpDamage = (int32(pdamage) + DoneTotal) * (damagetype == DOT ? 1.0f : SpellDamagePctDone(victim, spellProto, damagetype)); - // apply spellmod to Done damage (flat and pct) - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage); + float tmpDamage = float(int32(pdamage) + DoneTotal); + // SPELLMOD_DOT will be applied in AuraEffect::HandlePeriodicDamageAurasTick. + if (damagetype != DOT) + { + tmpDamage *= SpellDamagePctDone(victim, spellProto, damagetype); + // apply spellmod to Done damage (flat and pct) + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, tmpDamage); + } return uint32(std::max(tmpDamage, 0.0f)); } @@ -10793,11 +10812,15 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui DoneTotal = 0; } - // Done Percentage for DOT is already calculated, no need to do it again. The percentage mod is applied in Aura::HandleAuraSpecificMods. - float heal = float(int32(healamount) + DoneTotal) * (damagetype == DOT ? 1.0f : SpellHealingPctDone(victim, spellProto)); - // apply spellmod to Done amount - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal); + float heal = float(int32(healamount) + DoneTotal); + // SPELLMOD_DOT will be applied in AuraEffect::HandlePeriodicHealAurasTick. + if (damagetype != DOT) + { + heal *= SpellHealingPctDone(victim, spellProto); + // apply spellmod to Done amount + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, heal); + } return uint32(std::max(heal, 0.0f)); } @@ -11128,6 +11151,26 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo) const return false; } +uint32 Unit::GetSchoolImmunityMask() const +{ + uint32 mask = 0; + SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_SCHOOL]; + for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) + mask |= itr->type; + + return mask; +} + +uint32 Unit::GetMechanicImmunityMask() const +{ + uint32 mask = 0; + SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; + for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) + mask |= (1 << itr->type); + + return mask; +} + bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const { if (!spellInfo || !spellInfo->Effects[index].IsEffect()) @@ -12286,6 +12329,13 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced) /// @todo possible affect only on MOVE_RUN if (int32 normalization = GetMaxPositiveAuraModifier(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED)) { + if (Creature* creature = ToCreature()) + { + uint32 immuneMask = creature->GetCreatureTemplate()->MechanicImmuneMask; + if (immuneMask & (1 << MECHANIC_SNARE) || immuneMask & (1 << MECHANIC_DAZE)) + break; + } + // Use speed from aura float max_speed = normalization / (IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]); if (speed > max_speed) @@ -12307,15 +12357,15 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced) // Apply strongest slow aura mod to speed int32 slow = GetMaxNegativeAuraModifier(SPELL_AURA_MOD_DECREASE_SPEED); if (slow) - { AddPct(speed, slow); - if (float minSpeedMod = (float)GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MINIMUM_SPEED)) - { - float min_speed = minSpeedMod / 100.0f; - if (speed < min_speed) - speed = min_speed; - } + + if (float minSpeedMod = (float)GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MINIMUM_SPEED)) + { + float min_speed = minSpeedMod / 100.0f; + if (speed < min_speed) + speed = min_speed; } + SetSpeed(mtype, speed, forced); } @@ -16440,7 +16490,7 @@ void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ) float vcos, vsin; GetSinCos(x, y, vsin, vcos); - WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4)); + WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8 + 4 + 4 + 4 + 4 + 4)); data << GetPackGUID(); data << uint32(0); // counter data << float(vcos); // x direction @@ -16449,6 +16499,9 @@ void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ) data << float(-speedZ); // Z Movement speed (vertical) player->GetSession()->SendPacket(&data); + + if (player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || player->HasAuraType(SPELL_AURA_FLY)) + player->SetCanFly(true, true); } } @@ -17507,7 +17560,7 @@ bool Unit::SetSwim(bool enable) return true; } -bool Unit::SetCanFly(bool enable) +bool Unit::SetCanFly(bool enable, bool /*packetOnly = false */) { if (enable == HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY)) return false; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 088a0bbdf73..a61b406cbbd 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -273,13 +273,6 @@ enum UnitRename #define MAX_AGGRO_RESET_TIME 10 // in seconds #define MAX_AGGRO_RADIUS 45.0f // yards -enum Swing -{ - NOSWING = 0, - SINGLEHANDEDSWING = 1, - TWOHANDEDSWING = 2 -}; - enum VictimState { VICTIMSTATE_INTACT = 0, // set when attacker misses @@ -356,7 +349,7 @@ class TransportBase; class SpellCastTargets; typedef std::list<Unit*> UnitList; -typedef std::list< std::pair<Aura*, uint8> > DispelChargesList; +typedef std::list<std::pair<Aura*, uint8>> DispelChargesList; struct SpellImmune { @@ -381,13 +374,6 @@ enum WeaponDamageRange MAXDAMAGE }; -enum DamageTypeToSchool -{ - RESISTANCE, - DAMAGE_DEALT, - DAMAGE_TAKEN -}; - enum AuraRemoveMode { AURA_REMOVE_NONE = 0, @@ -1583,7 +1569,7 @@ class Unit : public WorldObject virtual bool SetWalk(bool enable); virtual bool SetDisableGravity(bool disable, bool packetOnly = false); virtual bool SetSwim(bool enable); - virtual bool SetCanFly(bool enable); + virtual bool SetCanFly(bool enable, bool packetOnly = false); virtual bool SetWaterWalking(bool enable, bool packetOnly = false); virtual bool SetFeatherFall(bool enable, bool packetOnly = false); virtual bool SetHover(bool enable, bool packetOnly = false); @@ -1896,7 +1882,7 @@ class Unit : public WorldObject void TauntApply(Unit* victim); void TauntFadeOut(Unit* taunter); ThreatManager& getThreatManager() { return m_ThreatManager; } - void addHatedBy(HostileReference* pHostileReference) { m_HostileRefManager.insertFirst(pHostileReference); }; + void addHatedBy(HostileReference* pHostileReference) { m_HostileRefManager.insertFirst(pHostileReference); } void removeHatedBy(HostileReference* /*pHostileReference*/) { /* nothing to do yet */ } HostileRefManager& getHostileRefManager() { return m_HostileRefManager; } @@ -1972,6 +1958,8 @@ class Unit : public WorldObject void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply); void ApplySpellDispelImmunity(const SpellInfo* spellProto, DispelType type, bool apply); virtual bool IsImmunedToSpell(SpellInfo const* spellInfo) const; // redefined in Creature + uint32 GetSchoolImmunityMask() const; + uint32 GetMechanicImmunityMask() const; bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const; bool IsImmunedToDamage(SpellInfo const* spellInfo) const; diff --git a/src/server/game/Events/GameEventMgr.h b/src/server/game/Events/GameEventMgr.h index a47b6f29e80..eb29e463a4d 100644 --- a/src/server/game/Events/GameEventMgr.h +++ b/src/server/game/Events/GameEventMgr.h @@ -96,7 +96,7 @@ class GameEventMgr { private: GameEventMgr(); - ~GameEventMgr() { }; + ~GameEventMgr() { } public: static GameEventMgr* instance() diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index a3414c72613..491a61f6f35 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -408,8 +408,8 @@ void ObjectMgr::LoadCreatureTemplates() "spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, " // 62 63 64 65 66 67 68 69 "InhabitType, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, " - // 70 71 72 73 74 75 76 77 78 79 80 - "questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName " + // 70 71 72 73 74 + "movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName " "FROM creature_template;"); if (!result) @@ -507,14 +507,11 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) creatureTemplate.ModExperience = fields[68].GetFloat(); creatureTemplate.RacialLeader = fields[69].GetBool(); - for (uint8 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) - creatureTemplate.questItems[i] = fields[70 + i].GetUInt32(); - - creatureTemplate.movementId = fields[76].GetUInt32(); - creatureTemplate.RegenHealth = fields[77].GetBool(); - creatureTemplate.MechanicImmuneMask = fields[78].GetUInt32(); - creatureTemplate.flags_extra = fields[79].GetUInt32(); - creatureTemplate.ScriptID = GetScriptId(fields[80].GetCString()); + creatureTemplate.movementId = fields[70].GetUInt32(); + creatureTemplate.RegenHealth = fields[71].GetBool(); + creatureTemplate.MechanicImmuneMask = fields[72].GetUInt32(); + creatureTemplate.flags_extra = fields[73].GetUInt32(); + creatureTemplate.ScriptID = GetScriptId(fields[74].GetCString()); } void ObjectMgr::LoadCreatureTemplateAddons() @@ -1057,12 +1054,12 @@ void ObjectMgr::LoadGameObjectAddons() { Field* fields = result->Fetch(); - ObjectGuid::LowType guid = fields[0].GetUInt64(); + ObjectGuid::LowType guid = fields[0].GetUInt32(); const GameObjectData* goData = GetGOData(guid); if (!goData) { - TC_LOG_ERROR("sql.sql", "GameObject (GUID: " UI64FMTD ") does not exist but has a record in `gameobject_addon`", guid); + TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) does not exist but has a record in `gameobject_addon`", guid); continue; } @@ -1072,14 +1069,14 @@ void ObjectMgr::LoadGameObjectAddons() if (gameObjectAddon.invisibilityType >= TOTAL_INVISIBILITY_TYPES) { - TC_LOG_ERROR("sql.sql", "GameObject (GUID: " UI64FMTD ") has invalid InvisibilityType in `gameobject_addon`", guid); + TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has invalid InvisibilityType in `gameobject_addon`", guid); gameObjectAddon.invisibilityType = INVISIBILITY_GENERAL; gameObjectAddon.InvisibilityValue = 0; } if (gameObjectAddon.invisibilityType && !gameObjectAddon.InvisibilityValue) { - TC_LOG_ERROR("sql.sql", "GameObject (GUID: " UI64FMTD ") has InvisibilityType set but has no InvisibilityValue in `gameobject_addon`, set to 1", guid); + TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has InvisibilityType set but has no InvisibilityValue in `gameobject_addon`, set to 1", guid); gameObjectAddon.InvisibilityValue = 1; } @@ -1147,8 +1144,8 @@ void ObjectMgr::LoadEquipmentTemplates() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 - QueryResult result = WorldDatabase.Query("SELECT entry, id, itemEntry1, itemEntry2, itemEntry3 FROM creature_equip_template"); + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT CreatureID, ID, ItemID1, ItemID2, ItemID3 FROM creature_equip_template"); if (!result) { @@ -1283,8 +1280,8 @@ CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32* display void ObjectMgr::LoadCreatureModelInfo() { uint32 oldMSTime = getMSTime(); - - QueryResult result = WorldDatabase.Query("SELECT modelid, bounding_radius, combat_reach, gender, modelid_other_gender FROM creature_model_info"); + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT DisplayID, BoundingRadius, CombatReach, Gender, DisplayID_Other_Gender FROM creature_model_info"); if (!result) { @@ -3800,8 +3797,8 @@ void ObjectMgr::LoadPlayerInfo() for (uint8 level = 0; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level) _playerXPperLevel[level] = 0; - // 0 1 - QueryResult result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level"); + // 0 1 + QueryResult result = WorldDatabase.Query("SELECT Level, Experience FROM player_xp_for_level"); if (!result) { @@ -3965,35 +3962,35 @@ void ObjectMgr::LoadQuests() mExclusiveQuestGroups.clear(); QueryResult result = WorldDatabase.Query("SELECT " - //0 1 2 3 4 5 6 7 8 9 10 11 12 - "Id, Method, Level, MinLevel, MaxLevel, ZoneOrSort, Type, SuggestedPlayers, LimitTime, RequiredClasses, RequiredRaces, RequiredSkillId, RequiredSkillPoints, " - // 13 14 15 16 17 18 19 20 - "RequiredFactionId1, RequiredFactionId2, RequiredFactionValue1, RequiredFactionValue2, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, " - // 21 22 23 24 25 26 27 28 29 30 31 - "PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestIdChain, RewardXPId, RewardOrRequiredMoney, RewardMoneyMaxLevel, RewardSpell, RewardSpellCast, RewardHonor, RewardHonorMultiplier, " - // 32 33 34 35 36 37 38 39 40 41 42 - "RewardMailTemplateId, RewardMailDelay, SourceItemId, SourceItemCount, SourceSpellId, Flags, SpecialFlags, RewardTitleId, RequiredPlayerKills, RewardTalents, RewardArenaPoints, " - // 43 44 45 46 47 48 49 50 - "RewardItemId1, RewardItemId2, RewardItemId3, RewardItemId4, RewardItemCount1, RewardItemCount2, RewardItemCount3, RewardItemCount4, " - // 51 52 53 54 55 56 57 58 59 60 61 62 - "RewardChoiceItemId1, RewardChoiceItemId2, RewardChoiceItemId3, RewardChoiceItemId4, RewardChoiceItemId5, RewardChoiceItemId6, RewardChoiceItemCount1, RewardChoiceItemCount2, RewardChoiceItemCount3, RewardChoiceItemCount4, RewardChoiceItemCount5, RewardChoiceItemCount6, " - // 63 64 65 66 67 68 69 70 71 72 - "RewardFactionId1, RewardFactionId2, RewardFactionId3, RewardFactionId4, RewardFactionId5, RewardFactionValueId1, RewardFactionValueId2, RewardFactionValueId3, RewardFactionValueId4, RewardFactionValueId5, " - // 73 74 75 76 77 - "RewardFactionValueIdOverride1, RewardFactionValueIdOverride2, RewardFactionValueIdOverride3, RewardFactionValueIdOverride4, RewardFactionValueIdOverride5, " - // 78 79 80 81 + //0 1 2 3 4 5 6 7 8 + "ID, Method, QuestLevel, MinLevel, QuestSortID, QuestType, SuggestedGroupNum, LimitTime, RequiredRaces," + // 9 10 11 12 + "RequiredFactionId1, RequiredFactionId2, RequiredFactionValue1, RequiredFactionValue2, " + // 13 14 15 16 17 18 19 20 + "NextQuestIdChain, RewardXPId, RewardOrRequiredMoney, RewardMoneyMaxLevel, RewardSpell, RewardSpellCast, RewardHonor, RewardHonorMultiplier, " + // 21 22 23 24 25 26 + "SourceItemId, Flags, RewardTitle, RequiredPlayerKills, RewardTalents, RewardArenaPoints, " + // 27 28 29 30 31 32 33 34 + "RewardItem1, RewardItem2, RewardItem3, RewardItem4, RewardAmount1, RewardAmount2, RewardAmount3, RewardAmount4, " + // 35 36 37 38 39 40 41 42 43 44 45 46 + "RewardChoiceItemID1, RewardChoiceItemID2, RewardChoiceItemID3, RewardChoiceItemID4, RewardChoiceItemID5, RewardChoiceItemID6, RewardChoiceItemQuantity1, RewardChoiceItemQuantity2, RewardChoiceItemQuantity3, RewardChoiceItemQuantity4, RewardChoiceItemQuantity5, RewardChoiceItemQuantity6, " + // 47 48 49 50 51 52 53 54 55 56 + "RewardFactionID1, RewardFactionID2, RewardFactionID3, RewardFactionID4, RewardFactionID5, RewardFactionValue1, RewardFactionValue2, RewardFactionValue3, RewardFactionValue4, RewardFactionValue5, " + // 57 58 59 60 61 + "RewardFactionOverride1, RewardFactionOverride2, RewardFactionOverride3, RewardFactionOverride4, RewardFactionOverride5, " + // 62 63 64 65 "PointMapId, PointX, PointY, PointOption, " - // 82 83 84 85 86 87 88 - "Title, Objectives, Details, EndText, OfferRewardText, RequestItemsText, CompletedText, " - // 89 90 91 92 93 94 95 96 + // 66 67 68 69 70 71 72 + "LogTitle, LogDescription, QuestDescription, EndText, OfferRewardText, RequestItemsText, QuestCompletionLog, " + // 73 74 75 76 77 78 79 80 "RequiredNpcOrGo1, RequiredNpcOrGo2, RequiredNpcOrGo3, RequiredNpcOrGo4, RequiredNpcOrGoCount1, RequiredNpcOrGoCount2, RequiredNpcOrGoCount3, RequiredNpcOrGoCount4, " - // 97 98 99 100 101 102 103 104 + // 81 82 83 84 85 86 87 88 "RequiredSourceItemId1, RequiredSourceItemId2, RequiredSourceItemId3, RequiredSourceItemId4, RequiredSourceItemCount1, RequiredSourceItemCount2, RequiredSourceItemCount3, RequiredSourceItemCount4, " - // 105 106 107 108 109 110 111 112 113 114 115 116 + // 89 90 91 92 93 94 95 96 97 98 99 100 "RequiredItemId1, RequiredItemId2, RequiredItemId3, RequiredItemId4, RequiredItemId5, RequiredItemId6, RequiredItemCount1, RequiredItemCount2, RequiredItemCount3, RequiredItemCount4, RequiredItemCount5, RequiredItemCount6, " - // 117 118 119 120 121 122 123 124 125 126 127 128 129 + // 101 102 103 104 105 106 107 108 109 110 111 112 113 "Unknown0, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4, DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, DetailsEmoteDelay1, DetailsEmoteDelay2, DetailsEmoteDelay3, DetailsEmoteDelay4, " - // 130 131 132 133 134 135 136 137 138 139 + // 114 115 116 117 118 119 120 121 122 123 "EmoteOnIncomplete, EmoteOnComplete, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4, OfferRewardEmoteDelay1, OfferRewardEmoteDelay2, OfferRewardEmoteDelay3, OfferRewardEmoteDelay4" " FROM quest_template"); if (!result) @@ -4015,6 +4012,31 @@ void ObjectMgr::LoadQuests() std::map<uint32, uint32> usedMailTemplates; + // Load `quest_template_addon` + // 0 1 2 3 4 5 6 7 8 + result = WorldDatabase.Query("SELECT ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, RewardMailTemplateID, RewardMailDelay, " + //9 10 11 12 13 14 15 16 + "RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags FROM quest_template_addon"); + + if (!result) + { + TC_LOG_ERROR("server.loading", ">> Loaded 0 quest template addons. DB table `quest_template_addon` is empty."); + } + else + { + do + { + Field* fields = result->Fetch(); + uint32 questId = fields[0].GetUInt32(); + + auto itr = _questTemplates.find(questId); + if (itr != _questTemplates.end()) + itr->second->LoadQuestTemplateAddon(fields); + else + TC_LOG_ERROR("server.loading", "Table `quest_template_addon` has data for quest %u but such quest does not exist", questId); + } while (result->NextRow()); + } + // Post processing for (QuestMap::iterator iter = _questTemplates.begin(); iter != _questTemplates.end(); ++iter) { @@ -5178,8 +5200,8 @@ void ObjectMgr::LoadPageTexts() { uint32 oldMSTime = getMSTime(); - // 0 1 2 - QueryResult result = WorldDatabase.Query("SELECT entry, text, next_page FROM page_text"); + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT ID, Text, NextPageID FROM page_text"); if (!result) { @@ -5392,14 +5414,14 @@ void ObjectMgr::LoadGossipText() uint32 oldMSTime = getMSTime(); QueryResult result = WorldDatabase.Query("SELECT ID, " - "text0_0, text0_1, BroadcastTextID0, lang0, prob0, em0_0, em0_1, em0_2, em0_3, em0_4, em0_5, " - "text1_0, text1_1, BroadcastTextID1, lang1, prob1, em1_0, em1_1, em1_2, em1_3, em1_4, em1_5, " - "text2_0, text2_1, BroadcastTextID2, lang2, prob2, em2_0, em2_1, em2_2, em2_3, em2_4, em2_5, " - "text3_0, text3_1, BroadcastTextID3, lang3, prob3, em3_0, em3_1, em3_2, em3_3, em3_4, em3_5, " - "text4_0, text4_1, BroadcastTextID4, lang4, prob4, em4_0, em4_1, em4_2, em4_3, em4_4, em4_5, " - "text5_0, text5_1, BroadcastTextID5, lang5, prob5, em5_0, em5_1, em5_2, em5_3, em5_4, em5_5, " - "text6_0, text6_1, BroadcastTextID6, lang6, prob6, em6_0, em6_1, em6_2, em6_3, em6_4, em6_5, " - "text7_0, text7_1, BroadcastTextID7, lang7, prob7, em7_0, em7_1, em7_2, em7_3, em7_4, em7_5 " + "text0_0, text0_1, BroadcastTextID0, lang0, Probability0, em0_0, em0_1, em0_2, em0_3, em0_4, em0_5, " + "text1_0, text1_1, BroadcastTextID1, lang1, Probability1, em1_0, em1_1, em1_2, em1_3, em1_4, em1_5, " + "text2_0, text2_1, BroadcastTextID2, lang2, Probability2, em2_0, em2_1, em2_2, em2_3, em2_4, em2_5, " + "text3_0, text3_1, BroadcastTextID3, lang3, Probability3, em3_0, em3_1, em3_2, em3_3, em3_4, em3_5, " + "text4_0, text4_1, BroadcastTextID4, lang4, Probability4, em4_0, em4_1, em4_2, em4_3, em4_4, em4_5, " + "text5_0, text5_1, BroadcastTextID5, lang5, Probability5, em5_0, em5_1, em5_2, em5_3, em5_4, em5_5, " + "text6_0, text6_1, BroadcastTextID6, lang6, Probability6, em6_0, em6_1, em6_2, em6_3, em6_4, em6_5, " + "text7_0, text7_1, BroadcastTextID7, lang7, Probability7, em7_0, em7_1, em7_2, em7_3, em7_4, em7_5 " "FROM npc_text"); @@ -6158,8 +6180,8 @@ void ObjectMgr::LoadAreaTriggerTeleports() _areaTriggerStore.clear(); // need for reload case - // 0 1 2 3 4 5 - QueryResult result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport"); + // 0 1 2 3 4 5 + QueryResult result = WorldDatabase.Query("SELECT ID, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport"); if (!result) { TC_LOG_INFO("server.loading", ">> Loaded 0 area trigger teleport definitions. DB table `areatrigger_teleport` is empty."); @@ -6596,12 +6618,12 @@ void ObjectMgr::LoadGameObjectTemplate() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 9 10 11 12 - QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, faction, flags, size, questItem1, questItem2, questItem3, " - // 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 - "questItem4, questItem5, questItem6, data0, data1, data2, data3, data4, data5, data6, data7, data8, data9, data10, data11, data12, " - // 29 30 31 32 33 34 35 36 37 38 39 40 41 - "data13, data14, data15, data16, data17, data18, data19, data20, data21, data22, data23, AIName, ScriptName " + // 0 1 2 3 4 5 6 7 8 9 + QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, faction, flags, size, " + // 10 11 12 13 14 15 16 17 18 19 20 21 22 + "Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, " + // 23 24 25 26 27 28 29 30 31 32 33 34 35 + "Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, AIName, ScriptName " "FROM gameobject_template"); if (!result) @@ -6631,14 +6653,11 @@ void ObjectMgr::LoadGameObjectTemplate() got.flags = fields[8].GetUInt32(); got.size = fields[9].GetFloat(); - for (uint8 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i) - got.questItems[i] = fields[10 + i].GetUInt32(); - for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i) - got.raw.data[i] = fields[16 + i].GetInt32(); // data1 and data6 can be -1 + got.raw.data[i] = fields[10 + i].GetInt32(); // data1 and data6 can be -1 - got.AIName = fields[40].GetString(); - got.ScriptId = GetScriptId(fields[41].GetCString()); + got.AIName = fields[34].GetString(); + got.ScriptId = GetScriptId(fields[35].GetCString()); // Checks @@ -7188,8 +7207,8 @@ void ObjectMgr::LoadPointsOfInterest() uint32 count = 0; - // 0 1 2 3 4 5 6 - QueryResult result = WorldDatabase.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest"); + // 0 1 2 3 4 5 6 + QueryResult result = WorldDatabase.Query("SELECT ID, PositionX, PositionY, Icon, Flags, Data, Name FROM points_of_interest"); if (!result) { @@ -7234,8 +7253,8 @@ void ObjectMgr::LoadQuestPOI() uint32 count = 0; - // 0 1 2 3 4 5 6 7 - QueryResult result = WorldDatabase.Query("SELECT questId, id, objIndex, mapid, WorldMapAreaId, FloorId, unk3, unk4 FROM quest_poi order by questId"); + // 0 1 2 3 4 5 6 7 + QueryResult result = WorldDatabase.Query("SELECT QuestID, id, ObjectiveIndex, MapID, WorldMapAreaId, Floor, Priority, Flags FROM quest_poi order by QuestID"); if (!result) { @@ -7243,8 +7262,8 @@ void ObjectMgr::LoadQuestPOI() return; } - // 0 1 2 3 - QueryResult points = WorldDatabase.Query("SELECT questId, id, x, y FROM quest_poi_points ORDER BY questId DESC, idx"); + // 0 1 2 3 + QueryResult points = WorldDatabase.Query("SELECT QuestID, Idx1, X, Y FROM quest_poi_points ORDER BY QuestID DESC, Idx2"); std::vector<std::vector<std::vector<QuestPOIPoint> > > POIs; @@ -8217,9 +8236,9 @@ void ObjectMgr::LoadTrainerSpell() // For reload case _cacheTrainerSpellStore.clear(); - QueryResult result = WorldDatabase.Query("SELECT b.entry, a.spell, a.spellcost, a.reqskill, a.reqskillvalue, a.reqlevel FROM npc_trainer AS a " - "INNER JOIN npc_trainer AS b ON a.entry = -(b.spell) " - "UNION SELECT * FROM npc_trainer WHERE spell > 0"); + QueryResult result = WorldDatabase.Query("SELECT b.ID, a.SpellID, a.MoneyCost, a.ReqSkillLine, a.ReqSkillRank, a.ReqLevel FROM npc_trainer AS a " + "INNER JOIN npc_trainer AS b ON a.ID = -(b.SpellID) " + "UNION SELECT * FROM npc_trainer WHERE SpellID > 0"); if (!result) { @@ -9122,3 +9141,63 @@ PlayerInfo const* ObjectMgr::GetPlayerInfo(uint32 race, uint32 class_) const return NULL; return info; } + +void ObjectMgr::LoadGameObjectQuestItems() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 + QueryResult result = WorldDatabase.Query("SELECT GameObjectEntry, ItemId FROM gameobject_questitem ORDER BY Idx ASC"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject quest items. DB table `gameobject_questitem` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + uint32 item = fields[1].GetUInt32(); + + _gameObjectQuestItemStore[entry].push_back(item); + + ++count; + } + while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u gameobject quest items in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + +void ObjectMgr::LoadCreatureQuestItems() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 + QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, ItemId FROM creature_questitem ORDER BY Idx ASC"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 creature quest items. DB table `creature_questitem` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + uint32 item = fields[1].GetUInt32(); + + _creatureQuestItemStore[entry].push_back(item); + + ++count; + } + while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u creature quest items in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index f18e39077f3..267a2c555a2 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -753,6 +753,24 @@ class ObjectMgr ObjectGuid GetPlayerGUIDByName(std::string const& name) const; + GameObjectQuestItemList const* GetGameObjectQuestItemList(uint32 id) const + { + GameObjectQuestItemMap::const_iterator itr = _gameObjectQuestItemStore.find(id); + if (itr != _gameObjectQuestItemStore.end()) + return &itr->second; + return NULL; + } + GameObjectQuestItemMap const* GetGameObjectQuestItemMap() const { return &_gameObjectQuestItemStore; } + + CreatureQuestItemList const* GetCreatureQuestItemList(uint32 id) const + { + CreatureQuestItemMap::const_iterator itr = _creatureQuestItemStore.find(id); + if (itr != _creatureQuestItemStore.end()) + return &itr->second; + return NULL; + } + CreatureQuestItemMap const* GetCreatureQuestItemMap() const { return &_creatureQuestItemStore; } + /** * Retrieves the player name by guid. * @@ -960,6 +978,8 @@ class ObjectMgr void LoadCreatureTemplateAddons(); void LoadCreatureTemplate(Field* fields); void CheckCreatureTemplate(CreatureTemplate const* cInfo); + void LoadGameObjectQuestItems(); + void LoadCreatureQuestItems(); void LoadTempSummons(); void LoadCreatures(); void LoadLinkedRespawn(); @@ -1412,6 +1432,8 @@ class ObjectMgr CreatureAddonContainer _creatureAddonStore; CreatureAddonContainer _creatureTemplateAddonStore; GameObjectAddonContainer _gameObjectAddonStore; + GameObjectQuestItemMap _gameObjectQuestItemStore; + CreatureQuestItemMap _creatureQuestItemStore; EquipmentInfoContainer _equipmentInfoStore; LinkedRespawnContainer _linkedRespawnStore; CreatureLocaleContainer _creatureLocaleStore; diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index ae8712510c9..f89c0e73008 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -936,7 +936,7 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject) continue; if (i->AllowedForPlayer(member)) { - if (member->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) + if (member->IsAtGroupRewardDistance(pLootedObject)) { r->totalPlayersRolling++; @@ -1021,7 +1021,7 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject) if (i->AllowedForPlayer(member)) { - if (member->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) + if (member->IsAtGroupRewardDistance(pLootedObject)) { r->totalPlayersRolling++; r->playerVote[member->GetGUID()] = NOT_EMITED_YET; @@ -1080,7 +1080,7 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject) continue; bool allowedForPlayer = i->AllowedForPlayer(playerToRoll); - if (allowedForPlayer && playerToRoll->IsWithinDistInMap(lootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) + if (allowedForPlayer && playerToRoll->IsAtGroupRewardDistance(lootedObject)) { r->totalPlayersRolling++; if (playerToRoll->GetPassOnGroupLoot()) @@ -1155,7 +1155,7 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject) continue; bool allowedForPlayer = i->AllowedForPlayer(playerToRoll); - if (allowedForPlayer && playerToRoll->IsWithinDistInMap(lootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) + if (allowedForPlayer && playerToRoll->IsAtGroupRewardDistance(lootedObject)) { r->totalPlayersRolling++; r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET; @@ -1231,7 +1231,7 @@ void Group::MasterLoot(Loot* loot, WorldObject* pLootedObject) if (!looter->IsInWorld()) continue; - if (looter->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) + if (looter->IsAtGroupRewardDistance(pLootedObject)) { data << uint64(looter->GetGUID()); ++real_count; @@ -1243,7 +1243,7 @@ void Group::MasterLoot(Loot* loot, WorldObject* pLootedObject) for (GroupReference* itr = GetFirstMember(); itr != NULL; itr = itr->next()) { Player* looter = itr->GetSource(); - if (looter->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) + if (looter->IsAtGroupRewardDistance(pLootedObject)) looter->GetSession()->SendPacket(&data); } } @@ -1743,7 +1743,7 @@ void Group::UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed) { // not update if only update if need and ok Player* looter = ObjectAccessor::FindPlayer(guid_itr->guid); - if (looter && looter->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) + if (looter && looter->IsAtGroupRewardDistance(pLootedObject)) return; } ++guid_itr; @@ -1754,7 +1754,7 @@ void Group::UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed) for (member_citerator itr = guid_itr; itr != m_memberSlots.end(); ++itr) { if (Player* player = ObjectAccessor::FindPlayer(itr->guid)) - if (player->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) + if (player->IsAtGroupRewardDistance(pLootedObject)) { pNewLooter = player; break; @@ -1767,7 +1767,7 @@ void Group::UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed) for (member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr) { if (Player* player = ObjectAccessor::FindPlayer(itr->guid)) - if (player->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) + if (player->IsAtGroupRewardDistance(pLootedObject)) { pNewLooter = player; break; @@ -1857,6 +1857,9 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const* // check if someone in party is using dungeon system if (member->isUsingLfg()) return ERR_LFG_CANT_USE_BATTLEGROUND; + // check Freeze debuff + if (member->HasAura(9454)) + return ERR_BATTLEGROUND_JOIN_FAILED; } // only check for MinPlayerCount since MinPlayerCount == MaxPlayerCount for arenas... diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index 28e933bd08c..732afce9517 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -218,7 +218,7 @@ class Group ObjectGuid GetMasterLooterGuid() const; ItemQualities GetLootThreshold() const; - uint32 GetDbStoreId() const { return m_dbStoreId; }; + uint32 GetDbStoreId() const { return m_dbStoreId; } // member manipulation methods bool IsMember(ObjectGuid guid) const; diff --git a/src/server/game/Groups/GroupMgr.cpp b/src/server/game/Groups/GroupMgr.cpp index 771fcb17b32..ab80a208765 100644 --- a/src/server/game/Groups/GroupMgr.cpp +++ b/src/server/game/Groups/GroupMgr.cpp @@ -124,7 +124,7 @@ void GroupMgr::LoadGroups() // 0 1 2 3 4 5 6 7 8 9 QueryResult result = CharacterDatabase.Query("SELECT g.leaderGuid, g.lootMethod, g.looterGuid, g.lootThreshold, g.icon1, g.icon2, g.icon3, g.icon4, g.icon5, g.icon6" // 10 11 12 13 14 15 16 17 18 - ", g.icon7, g.icon8, g.groupType, g.difficulty, g.raiddifficulty, g.masterLooterGuid, g.guid, lfg.dungeon, lfg.state FROM groups g LEFT JOIN lfg_data lfg ON lfg.guid = g.guid ORDER BY g.guid ASC"); + ", g.icon7, g.icon8, g.groupType, g.difficulty, g.raidDifficulty, g.masterLooterGuid, g.guid, lfg.dungeon, lfg.state FROM groups g LEFT JOIN lfg_data lfg ON lfg.guid = g.guid ORDER BY g.guid ASC"); if (!result) { TC_LOG_INFO("server.loading", ">> Loaded 0 group definitions. DB table `groups` is empty!"); diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index de23626eb25..2ec89ac2a26 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -178,6 +178,10 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) return; } + // check Freeze debuff + if (_player->HasAura(9454)) + return; + BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, NULL, bgTypeId, bracketEntry, 0, false, isPremade, 0, 0); @@ -434,6 +438,10 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recvData) WorldPacket data; if (action) { + // check Freeze debuff + if (_player->HasAura(9454)) + return; + if (!_player->IsInvitedForBattlegroundQueueType(bgQueueTypeId)) return; // cheating? diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index b4f19234df9..26d1737257e 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -176,7 +176,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPacket& /*recvData*/) if (!member) continue; - if (player->IsWithinDistInMap(member, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) + if (player->IsAtGroupRewardDistance(member)) playersNear.push_back(member); } diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index d15f21f4ad3..03782481ef7 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -131,8 +131,15 @@ void WorldSession::HandleCreatureQueryOpcode(WorldPacket& recvData) data << float(ci->ModHealth); // dmg/hp modifier data << float(ci->ModMana); // dmg/mana modifier data << uint8(ci->RacialLeader); - for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) - data << uint32(ci->questItems[i]); // itemId[6], quest drop + + CreatureQuestItemList const* items = sObjectMgr->GetCreatureQuestItemList(entry); + if (items) + for (size_t i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) + data << (i < items->size() ? uint32((*items)[i]) : uint32(0)); + else + for (size_t i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) + data << uint32(0); + data << uint32(ci->movementId); // CreatureMovementInfo.dbc SendPacket(&data); TC_LOG_DEBUG("network", "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE"); @@ -188,8 +195,15 @@ void WorldSession::HandleGameObjectQueryOpcode(WorldPacket& recvData) data << info->unk1; // 2.0.3, string data.append(info->raw.data, MAX_GAMEOBJECT_DATA); data << float(info->size); // go size - for (uint32 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i) - data << uint32(info->questItems[i]); // itemId[6], quest drop + + GameObjectQuestItemList const* items = sObjectMgr->GetGameObjectQuestItemList(entry); + if (items) + for (size_t i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i) + data << (i < items->size() ? uint32((*items)[i]) : uint32(0)); + else + for (size_t i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i) + data << uint32(0); + SendPacket(&data); TC_LOG_DEBUG("network", "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE"); } diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index ba4e4713458..c825db1e1eb 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -151,14 +151,8 @@ uint32 LootStore::LoadLootTable() bool needsquest = fields[4].GetBool(); uint16 lootmode = fields[5].GetUInt16(); uint8 groupid = fields[6].GetUInt8(); - int32 mincount = fields[7].GetUInt8(); - int32 maxcount = fields[8].GetUInt8(); - - if (maxcount > std::numeric_limits<uint8>::max()) - { - TC_LOG_ERROR("sql.sql", "Table '%s' Entry %d Item %d: MaxCount value (%u) to large. must be less %u - skipped", GetName(), entry, item, maxcount, std::numeric_limits<uint8>::max()); - continue; // error already printed to log/console. - } + uint8 mincount = fields[7].GetUInt8(); + uint8 maxcount = fields[8].GetUInt8(); if (groupid >= 1 << 7) // it stored in 7 bit field { diff --git a/src/server/game/Maps/ZoneScript.h b/src/server/game/Maps/ZoneScript.h index d8dba57d438..fd52bdd4c14 100644 --- a/src/server/game/Maps/ZoneScript.h +++ b/src/server/game/Maps/ZoneScript.h @@ -29,8 +29,8 @@ class ZoneScript ZoneScript() { } virtual ~ZoneScript() { } - virtual uint32 GetCreatureEntry(uint32 /*guidlow*/, CreatureData const* data) { return data->id; } - virtual uint32 GetGameObjectEntry(uint32 /*guidlow*/, uint32 entry) { return entry; } + virtual uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) { return data->id; } + virtual uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidLow*/, uint32 entry) { return entry; } virtual void OnCreatureCreate(Creature* ) { } virtual void OnCreatureRemove(Creature* ) { } diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 9bae8fbdfae..63390ad61da 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -971,15 +971,15 @@ enum TrinityStrings LANG_GUILD_INFO_BANK_GOLD = 1181, LANG_GUILD_INFO_MOTD = 1182, LANG_GUILD_INFO_EXTRA_INFO = 1183, - // Room for more level 3 1184-1199 not used + // Room for more level 3 1184-1198 not used // Debug commands + LANG_DO_NOT_USE_6X_DEBUG_AREATRIGGER_LEFT = 1999, LANG_CINEMATIC_NOT_EXIST = 1200, LANG_MOVIE_NOT_EXIST = 1201, LANG_DEBUG_AREATRIGGER_ON = 1202, LANG_DEBUG_AREATRIGGER_OFF = 1203, LANG_DEBUG_AREATRIGGER_REACHED = 1204, - // Room for more debug 1205-1299 not used // Isle of Conquest LANG_BG_IC_START_TWO_MINUTES = 1205, diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 0b547d96e7f..c8da50364ba 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -182,7 +182,7 @@ class MotionMaster //: private std::stack<MovementGenerator *> void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ); void MoveJumpTo(float angle, float speedXY, float speedZ); void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = EVENT_JUMP) - { MoveJump(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speedXY, speedZ, id); }; + { 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 MoveFall(uint32 id = 0); diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp index afd1f73c785..2aebac6beb4 100644 --- a/src/server/game/Movement/PathGenerator.cpp +++ b/src/server/game/Movement/PathGenerator.cpp @@ -116,7 +116,7 @@ dtPolyRef PathGenerator::GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 } if (distance) - *distance = dtSqrt(minDist3d); + *distance = dtMathSqrtf(minDist3d); return (minDist2d < 3.0f) ? nearestPoly : INVALID_POLYREF; } @@ -799,7 +799,7 @@ dtStatus PathGenerator::FindSmoothPath(float const* startPos, float const* endPo // Find movement delta. float delta[VERTEX_SIZE]; dtVsub(delta, steerPos, iterPos); - float len = dtSqrt(dtVdot(delta, delta)); + float len = dtMathSqrtf(dtVdot(delta, delta)); // If the steer target is end of path or off-mesh link, do not move past the location. if ((endOfPath || offMeshConnection) && len < SMOOTH_PATH_STEP_SIZE) len = 1.0f; diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp index 6a6e0f45eb7..d1dcfd0f693 100644 --- a/src/server/game/Quests/QuestDef.cpp +++ b/src/server/game/Quests/QuestDef.cpp @@ -26,121 +26,102 @@ Quest::Quest(Field* questRecord) Method = questRecord[1].GetUInt8(); Level = questRecord[2].GetInt16(); MinLevel = questRecord[3].GetUInt8(); - MaxLevel = questRecord[4].GetUInt8(); - ZoneOrSort = questRecord[5].GetInt16(); - Type = questRecord[6].GetUInt16(); - SuggestedPlayers = questRecord[7].GetUInt8(); - LimitTime = questRecord[8].GetUInt32(); - RequiredClasses = questRecord[9].GetUInt16(); - RequiredRaces = questRecord[10].GetUInt16(); - RequiredSkillId = questRecord[11].GetUInt16(); - RequiredSkillPoints = questRecord[12].GetUInt16(); - RequiredFactionId1 = questRecord[13].GetUInt16(); - RequiredFactionId2 = questRecord[14].GetUInt16(); - RequiredFactionValue1 = questRecord[15].GetInt32(); - RequiredFactionValue2 = questRecord[16].GetInt32(); - RequiredMinRepFaction = questRecord[17].GetUInt16(); - RequiredMaxRepFaction = questRecord[18].GetUInt16(); - RequiredMinRepValue = questRecord[19].GetInt32(); - RequiredMaxRepValue = questRecord[20].GetInt32(); - PrevQuestId = questRecord[21].GetInt32(); - NextQuestId = questRecord[22].GetInt32(); - ExclusiveGroup = questRecord[23].GetInt32(); - NextQuestIdChain = questRecord[24].GetUInt32(); - RewardXPId = questRecord[25].GetUInt8(); - RewardOrRequiredMoney = questRecord[26].GetInt32(); - RewardMoneyMaxLevel = questRecord[27].GetUInt32(); - RewardSpell = questRecord[28].GetUInt32(); - RewardSpellCast = questRecord[29].GetInt32(); - RewardHonor = questRecord[30].GetUInt32(); - RewardHonorMultiplier = questRecord[31].GetFloat(); - RewardMailTemplateId = questRecord[32].GetUInt32(); - RewardMailDelay = questRecord[33].GetUInt32(); - SourceItemId = questRecord[34].GetUInt32(); - SourceItemIdCount = questRecord[35].GetUInt8(); - SourceSpellid = questRecord[36].GetUInt32(); - Flags = questRecord[37].GetUInt32(); - SpecialFlags = questRecord[38].GetUInt8(); - RewardTitleId = questRecord[39].GetUInt8(); - RequiredPlayerKills = questRecord[40].GetUInt8(); - RewardTalents = questRecord[41].GetUInt8(); - RewardArenaPoints = questRecord[42].GetUInt16(); + ZoneOrSort = questRecord[4].GetInt16(); + Type = questRecord[5].GetUInt16(); + SuggestedPlayers = questRecord[6].GetUInt8(); + LimitTime = questRecord[7].GetUInt32(); + RequiredRaces = questRecord[8].GetUInt16(); + RequiredFactionId1 = questRecord[9].GetUInt16(); + RequiredFactionId2 = questRecord[10].GetUInt16(); + RequiredFactionValue1 = questRecord[11].GetInt32(); + RequiredFactionValue2 = questRecord[12].GetInt32(); + NextQuestIdChain = questRecord[13].GetUInt32(); + RewardXPId = questRecord[14].GetUInt8(); + RewardOrRequiredMoney = questRecord[15].GetInt32(); + RewardMoneyMaxLevel = questRecord[16].GetUInt32(); + RewardSpell = questRecord[17].GetUInt32(); + RewardSpellCast = questRecord[18].GetInt32(); + RewardHonor = questRecord[19].GetUInt32(); + RewardHonorMultiplier = questRecord[20].GetFloat(); + SourceItemId = questRecord[21].GetUInt32(); + Flags = questRecord[22].GetUInt32(); + RewardTitleId = questRecord[23].GetUInt8(); + RequiredPlayerKills = questRecord[24].GetUInt8(); + RewardTalents = questRecord[25].GetUInt8(); + RewardArenaPoints = questRecord[26].GetUInt16(); for (int i = 0; i < QUEST_REWARDS_COUNT; ++i) - RewardItemId[i] = questRecord[43+i].GetUInt32(); + RewardItemId[i] = questRecord[27+i].GetUInt32(); for (int i = 0; i < QUEST_REWARDS_COUNT; ++i) - RewardItemIdCount[i] = questRecord[47+i].GetUInt16(); + RewardItemIdCount[i] = questRecord[31+i].GetUInt16(); for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) - RewardChoiceItemId[i] = questRecord[51+i].GetUInt32(); + RewardChoiceItemId[i] = questRecord[35+i].GetUInt32(); for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) - RewardChoiceItemCount[i] = questRecord[57+i].GetUInt16(); + RewardChoiceItemCount[i] = questRecord[41+i].GetUInt16(); for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) - RewardFactionId[i] = questRecord[63+i].GetUInt16(); + RewardFactionId[i] = questRecord[47+i].GetUInt16(); for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) - RewardFactionValueId[i] = questRecord[68+i].GetInt32(); + RewardFactionValueId[i] = questRecord[52+i].GetInt32(); for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) - RewardFactionValueIdOverride[i] = questRecord[73+i].GetInt32(); - - PointMapId = questRecord[78].GetUInt16(); - PointX = questRecord[79].GetFloat(); - PointY = questRecord[80].GetFloat(); - PointOption = questRecord[81].GetUInt32(); - Title = questRecord[82].GetString(); - Objectives = questRecord[83].GetString(); - Details = questRecord[84].GetString(); - EndText = questRecord[85].GetString(); - OfferRewardText = questRecord[86].GetString(); - RequestItemsText = questRecord[87].GetString(); - CompletedText = questRecord[88].GetString(); + RewardFactionValueIdOverride[i] = questRecord[57+i].GetInt32(); + + PointMapId = questRecord[62].GetUInt16(); + PointX = questRecord[63].GetFloat(); + PointY = questRecord[64].GetFloat(); + PointOption = questRecord[65].GetUInt32(); + Title = questRecord[66].GetString(); + Objectives = questRecord[67].GetString(); + Details = questRecord[68].GetString(); + EndText = questRecord[69].GetString(); + OfferRewardText = questRecord[70].GetString(); + RequestItemsText = questRecord[71].GetString(); + CompletedText = questRecord[72].GetString(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - RequiredNpcOrGo[i] = questRecord[89+i].GetInt32(); + RequiredNpcOrGo[i] = questRecord[73+i].GetInt32(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - RequiredNpcOrGoCount[i] = questRecord[93+i].GetUInt16(); + RequiredNpcOrGoCount[i] = questRecord[77+i].GetUInt16(); for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - RequiredSourceItemId[i] = questRecord[97+i].GetUInt32(); + RequiredSourceItemId[i] = questRecord[81+i].GetUInt32(); for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - RequiredSourceItemCount[i] = questRecord[101+i].GetUInt16(); + RequiredSourceItemCount[i] = questRecord[85+i].GetUInt16(); for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - RequiredItemId[i] = questRecord[105+i].GetUInt32(); + RequiredItemId[i] = questRecord[89+i].GetUInt32(); for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - RequiredItemCount[i] = questRecord[111+i].GetUInt16(); + RequiredItemCount[i] = questRecord[95+i].GetUInt16(); - // int8 Unknown0 = questRecord[117].GetUInt8(); + // int8 Unknown0 = questRecord[101].GetUInt8(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ObjectiveText[i] = questRecord[118+i].GetString(); + ObjectiveText[i] = questRecord[102+i].GetString(); for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) - DetailsEmote[i] = questRecord[122+i].GetUInt16(); + DetailsEmote[i] = questRecord[106+i].GetUInt16(); for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) - DetailsEmoteDelay[i] = questRecord[126+i].GetUInt32(); + DetailsEmoteDelay[i] = questRecord[110+i].GetUInt32(); - EmoteOnIncomplete = questRecord[130].GetUInt16(); - EmoteOnComplete = questRecord[131].GetUInt16(); + EmoteOnIncomplete = questRecord[114].GetUInt16(); + EmoteOnComplete = questRecord[115].GetUInt16(); for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) - OfferRewardEmote[i] = questRecord[132+i].GetInt16(); + OfferRewardEmote[i] = questRecord[116+i].GetInt16(); for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) - OfferRewardEmoteDelay[i] = questRecord[136+i].GetInt32(); + OfferRewardEmoteDelay[i] = questRecord[120+i].GetInt32(); - //int32 VerifiedBuild = questRecord[140].GetInt32(); - - if (SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT) - Flags |= QUEST_FLAGS_AUTO_ACCEPT; + //int32 VerifiedBuild = questRecord[124].GetInt32(); _reqItemsCount = 0; _reqCreatureOrGOcount = 0; @@ -164,6 +145,29 @@ Quest::Quest(Field* questRecord) ++_rewChoiceItemsCount; } +void Quest::LoadQuestTemplateAddon(Field* fields) +{ + MaxLevel = fields[1].GetUInt8(); + RequiredClasses = fields[2].GetUInt32(); + SourceSpellid = fields[3].GetUInt32(); + PrevQuestId = fields[4].GetInt32(); + NextQuestId = fields[5].GetInt32(); + ExclusiveGroup = fields[6].GetInt32(); + RewardMailTemplateId = fields[7].GetUInt32(); + RewardMailDelay = fields[8].GetUInt32(); + RequiredSkillId = fields[9].GetUInt16(); + RequiredSkillPoints = fields[10].GetUInt16(); + RequiredMinRepFaction = fields[11].GetUInt16(); + RequiredMaxRepFaction = fields[12].GetUInt16(); + RequiredMinRepValue = fields[13].GetInt32(); + RequiredMaxRepValue = fields[14].GetInt32(); + SourceItemIdCount = fields[15].GetUInt8(); + SpecialFlags = fields[16].GetUInt8(); + + if (SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT) + Flags |= QUEST_FLAGS_AUTO_ACCEPT; +} + uint32 Quest::XPValue(Player* player) const { if (player) diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h index 8485fab7f63..fdfb2a8e8b9 100644 --- a/src/server/game/Quests/QuestDef.h +++ b/src/server/game/Quests/QuestDef.h @@ -195,6 +195,8 @@ class Quest friend class ObjectMgr; public: Quest(Field* questRecord); + void LoadQuestTemplateAddon(Field* fields); + uint32 XPValue(Player* player) const; bool HasFlag(uint32 flag) const { return (Flags & flag) != 0; } @@ -315,21 +317,13 @@ class Quest uint32 Method; int32 ZoneOrSort; uint32 MinLevel; - uint32 MaxLevel; int32 Level; uint32 Type; - uint32 RequiredClasses; uint32 RequiredRaces; - uint32 RequiredSkillId; - uint32 RequiredSkillPoints; uint32 RequiredFactionId1; int32 RequiredFactionValue1; uint32 RequiredFactionId2; int32 RequiredFactionValue2; - uint32 RequiredMinRepFaction; - int32 RequiredMinRepValue; - uint32 RequiredMaxRepFaction; - int32 RequiredMaxRepValue; uint32 SuggestedPlayers; uint32 LimitTime; uint32 Flags; @@ -337,14 +331,9 @@ class Quest uint32 RequiredPlayerKills; uint32 RewardTalents; int32 RewardArenaPoints; - int32 PrevQuestId; - int32 NextQuestId; - int32 ExclusiveGroup; uint32 NextQuestIdChain; uint32 RewardXPId; uint32 SourceItemId; - uint32 SourceItemIdCount; - uint32 SourceSpellid; std::string Title; std::string Details; std::string Objectives; @@ -358,8 +347,6 @@ class Quest uint32 RewardMoneyMaxLevel; uint32 RewardSpell; int32 RewardSpellCast; - uint32 RewardMailTemplateId; - uint32 RewardMailDelay; uint32 PointMapId; float PointX; float PointY; @@ -367,7 +354,23 @@ class Quest uint32 EmoteOnIncomplete; uint32 EmoteOnComplete; - uint32 SpecialFlags; // custom flags, not sniffed/WDB + // quest_template_addon table (custom data) + uint32 MaxLevel = 0; + uint32 RequiredClasses = 0; + uint32 SourceSpellid = 0; + int32 PrevQuestId = 0; + int32 NextQuestId = 0; + int32 ExclusiveGroup = 0; + uint32 RewardMailTemplateId = 0; + uint32 RewardMailDelay = 0; + uint32 RequiredSkillId = 0; + uint32 RequiredSkillPoints = 0; + uint32 RequiredMinRepFaction = 0; + int32 RequiredMinRepValue = 0; + uint32 RequiredMaxRepFaction = 0; + int32 RequiredMaxRepValue = 0; + uint32 SourceItemIdCount = 0; + uint32 SpecialFlags = 0; // custom flags, not sniffed/WDB }; struct QuestStatusData diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 378f5f0d622..286cdf51bf1 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -3020,6 +3020,10 @@ void AuraEffect::HandleModStateImmunityMask(AuraApplication const* aurApp, uint8 switch (miscVal) { + case 27: + target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SILENCE, apply); + aura_immunity_list.push_back(SPELL_AURA_MOD_SILENCE); + break; case 96: case 1615: { @@ -5845,6 +5849,9 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const else damage = std::max(int32(damage * GetDonePct()), 0); + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(GetSpellInfo()->Id, SPELLMOD_DOT, damage); + damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); // Calculate armor mitigation @@ -6148,6 +6155,9 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const else damage = std::max(int32(damage * GetDonePct()), 0); + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(GetSpellInfo()->Id, SPELLMOD_DOT, damage); + damage = target->SpellHealingBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); } diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 5479dcdbf00..e6f01dea33e 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -1305,6 +1305,7 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b if (AuraEffect const* aurEff = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, 3790, 1)) { uint32 damage = caster->SpellDamageBonusDone(target, GetSpellInfo(), GetEffect(0)->GetAmount(), DOT); + damage *= caster->SpellDamagePctDone(target, GetSpellInfo(), SPELL_DIRECT_DAMAGE); damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DOT); int32 basepoints0 = aurEff->GetAmount() * GetEffect(0)->GetTotalTicks() * int32(damage) / 100; int32 heal = int32(CalculatePct(basepoints0, 15)); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 565f3fda8ac..00a28f22db4 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3754,6 +3754,10 @@ void Spell::SendSpellStart() //TC_LOG_DEBUG("spells", "Sending SMSG_SPELL_START id=%u", m_spellInfo->Id); uint32 castFlags = CAST_FLAG_UNKNOWN_2; + uint32 schoolImmunityMask = m_caster->GetSchoolImmunityMask(); + uint32 mechanicImmunityMask = m_caster->GetMechanicImmunityMask(); + if (schoolImmunityMask || mechanicImmunityMask) + castFlags |= CAST_FLAG_IMMUNITY; if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) castFlags |= CAST_FLAG_PENDING; @@ -3788,10 +3792,10 @@ void Spell::SendSpellStart() if (castFlags & CAST_FLAG_AMMO) WriteAmmoToPacket(&data); - if (castFlags & CAST_FLAG_UNKNOWN_23) + if (castFlags & CAST_FLAG_IMMUNITY) { - data << uint32(0); - data << uint32(0); + data << uint32(schoolImmunityMask); + data << uint32(mechanicImmunityMask); } m_caster->SendMessageToSet(&data, true); @@ -5087,12 +5091,12 @@ SpellCastResult Spell::CheckCast(bool strict) bool result = m_preGeneratedPath.CalculatePath(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + target->GetObjectSize(), false, true); if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT) return SPELL_FAILED_OUT_OF_RANGE; - else if (!result || m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH) + else if (!result || m_preGeneratedPath.GetPathType() & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE)) { result = m_preGeneratedPath.CalculatePath(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + target->GetObjectSize(), false, false); if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT) return SPELL_FAILED_OUT_OF_RANGE; - else if (!result || m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH) + else if (!result || m_preGeneratedPath.GetPathType() & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE)) return SPELL_FAILED_NOPATH; } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 3829e12b790..965b2463139 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3100,19 +3100,18 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/) if (m_spellInfo->Id == 62124) m_caster->CastSpell(unitTarget, 67485, true); - // Also use this effect to set the taunter's threat to the taunted creature's highest value - if (unitTarget->getThreatManager().getCurrentVictim()) + if (!unitTarget->getThreatManager().getOnlineContainer().empty()) { + // Also use this effect to set the taunter's threat to the taunted creature's highest value float myThreat = unitTarget->getThreatManager().getThreat(m_caster); - float itsThreat = unitTarget->getThreatManager().getCurrentVictim()->getThreat(); - if (itsThreat > myThreat) - unitTarget->getThreatManager().addThreat(m_caster, itsThreat - myThreat); - } + float topThreat = unitTarget->getThreatManager().getOnlineContainer().getMostHated()->getThreat(); + if (topThreat > myThreat) + unitTarget->getThreatManager().doAddThreat(m_caster, topThreat - myThreat); - //Set aggro victim to caster - if (!unitTarget->getThreatManager().getOnlineContainer().empty()) + //Set aggro victim to caster if (HostileReference* forcedVictim = unitTarget->getThreatManager().getOnlineContainer().getReferenceByTarget(m_caster)) unitTarget->getThreatManager().setCurrentVictim(forcedVictim); + } if (unitTarget->ToCreature()->IsAIEnabled && !unitTarget->ToCreature()->HasReactState(REACT_PASSIVE)) unitTarget->ToCreature()->AI()->AttackStart(m_caster); diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 75a48e36ded..ea5e4c8df0b 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1489,6 +1489,7 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a if (player && !player->CanFlyInZone(map_id, zone_id)) return SPELL_FAILED_INCORRECT_AREA; } + break; } } } @@ -1614,7 +1615,7 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta } // not allow casting on flying player - if (unitTarget->HasUnitState(UNIT_STATE_IN_FLIGHT)) + if (unitTarget->HasUnitState(UNIT_STATE_IN_FLIGHT) && !(AttributesCu & SPELL_ATTR0_CU_ALLOW_INFLIGHT_TARGET)) return SPELL_FAILED_BAD_TARGETS; /* TARGET_UNIT_MASTER gets blocked here for passengers, because the whole idea of this check is to diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index f2808a089e1..cfa9877405d 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -186,6 +186,7 @@ enum SpellCustomAttributes SPELL_ATTR0_CU_IGNORE_ARMOR = 0x00008000, SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER = 0x00010000, SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET = 0x00020000, + SPELL_ATTR0_CU_ALLOW_INFLIGHT_TARGET = 0x00040000, SPELL_ATTR0_CU_NEGATIVE = SPELL_ATTR0_CU_NEGATIVE_EFF0 | SPELL_ATTR0_CU_NEGATIVE_EFF1 | SPELL_ATTR0_CU_NEGATIVE_EFF2 }; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 13290320084..53b44fdd5cc 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -1609,8 +1609,8 @@ void SpellMgr::LoadSpellTargetPositions() mSpellTargetPositions.clear(); // need for reload case - // 0 1 2 3 4 5 6 - QueryResult result = WorldDatabase.Query("SELECT id, effIndex, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM spell_target_position"); + // 0 1 2 3 4 5 6 + QueryResult result = WorldDatabase.Query("SELECT ID, EffectIndex, MapID, PositionX, PositionY, PositionZ, Orientation FROM spell_target_position"); if (!result) { TC_LOG_INFO("server.loading", ">> Loaded 0 spell target coordinates. DB table `spell_target_position` is empty."); diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index b347a982132..09f557dc63b 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -641,8 +641,8 @@ SpellValue const* SpellScript::GetSpellValue() bool AuraScript::_Validate(SpellInfo const* entry) { for (std::list<CheckAreaTargetHandler>::iterator itr = DoCheckAreaTarget.begin(); itr != DoCheckAreaTarget.end(); ++itr) - if (!entry->HasAreaAuraEffect() && !entry->HasEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA)) - TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have area aura effect - handler bound to hook `DoCheckAreaTarget` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); + if (!entry->HasAreaAuraEffect() && !entry->HasEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) && !entry->HasEffect(SPELL_EFFECT_APPLY_AURA)) + TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoCheckAreaTarget` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); for (std::list<AuraDispelHandler>::iterator itr = OnDispel.begin(); itr != OnDispel.end(); ++itr) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) diff --git a/src/server/game/Texts/CreatureTextMgr.cpp b/src/server/game/Texts/CreatureTextMgr.cpp index 5fbc4b8ae1e..f9ccd2efd16 100644 --- a/src/server/game/Texts/CreatureTextMgr.cpp +++ b/src/server/game/Texts/CreatureTextMgr.cpp @@ -77,7 +77,7 @@ void CreatureTextMgr::LoadCreatureTexts() uint32 oldMSTime = getMSTime(); mTextMap.clear(); // for reload case - mTextRepeatMap.clear(); //reset all currently used temp texts + //all currently used temp texts are NOT reset PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEXT); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -224,13 +224,7 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject if (tempGroup.empty()) { - CreatureTextRepeatMap::iterator mapItr = mTextRepeatMap.find(source->GetGUID()); - if (mapItr != mTextRepeatMap.end()) - { - CreatureTextRepeatGroup::iterator groupItr = mapItr->second.find(textGroup); - groupItr->second.clear(); - } - + source->ClearTextRepeatGroup(textGroup); tempGroup = textGroupContainer; } @@ -426,26 +420,14 @@ void CreatureTextMgr::SetRepeatId(Creature* source, uint8 textGroup, uint8 id) if (!source) return; - CreatureTextRepeatIds& repeats = mTextRepeatMap[source->GetGUID()][textGroup]; - if (std::find(repeats.begin(), repeats.end(), id) == repeats.end()) - repeats.push_back(id); - else - TC_LOG_ERROR("sql.sql", "CreatureTextMgr: TextGroup %u for Creature(%s) GuidLow %u Entry %u, id %u already added", uint32(textGroup), source->GetName().c_str(), source->GetGUIDLow(), source->GetEntry(), uint32(id)); + source->SetTextRepeatId(textGroup, id); } CreatureTextRepeatIds CreatureTextMgr::GetRepeatGroup(Creature* source, uint8 textGroup) { ASSERT(source);//should never happen - CreatureTextRepeatIds ids; - CreatureTextRepeatMap::const_iterator mapItr = mTextRepeatMap.find(source->GetGUID()); - if (mapItr != mTextRepeatMap.end()) - { - CreatureTextRepeatGroup::const_iterator groupItr = (*mapItr).second.find(textGroup); - if (groupItr != mapItr->second.end()) - ids = groupItr->second; - } - return ids; + return source->GetTextRepeatGroup(textGroup); } bool CreatureTextMgr::TextExist(uint32 sourceEntry, uint8 textGroup) diff --git a/src/server/game/Texts/CreatureTextMgr.h b/src/server/game/Texts/CreatureTextMgr.h index 6ee1e82ce66..237aedd49d0 100644 --- a/src/server/game/Texts/CreatureTextMgr.h +++ b/src/server/game/Texts/CreatureTextMgr.h @@ -75,11 +75,6 @@ typedef std::unordered_map<uint32, CreatureTextHolder> CreatureTextMap; // a typedef std::map<CreatureTextId, CreatureTextLocale> LocaleCreatureTextMap; -//used for handling non-repeatable random texts -typedef std::vector<uint8> CreatureTextRepeatIds; -typedef std::unordered_map<uint8, CreatureTextRepeatIds> CreatureTextRepeatGroup; -typedef std::unordered_map<ObjectGuid, CreatureTextRepeatGroup> CreatureTextRepeatMap;//guid based - class CreatureTextMgr { private: @@ -115,7 +110,6 @@ class CreatureTextMgr float GetRangeForChatType(ChatMsg msgType) const; CreatureTextMap mTextMap; - CreatureTextRepeatMap mTextRepeatMap; LocaleCreatureTextMap mLocaleTextMap; }; diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp index db37989bfc3..3d26f349ca5 100644 --- a/src/server/game/Tickets/TicketMgr.cpp +++ b/src/server/game/Tickets/TicketMgr.cpp @@ -296,7 +296,7 @@ void TicketMgr::LoadTickets() PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) { - TC_LOG_INFO("server.loading", ">> Loaded 0 GM tickets. DB table `gm_tickets` is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded 0 GM tickets. DB table `gm_ticket` is empty!"); return; } @@ -333,7 +333,7 @@ void TicketMgr::LoadSurveys() _lastSurveyId = 0; uint32 oldMSTime = getMSTime(); - if (QueryResult result = CharacterDatabase.Query("SELECT MAX(surveyId) FROM gm_surveys")) + if (QueryResult result = CharacterDatabase.Query("SELECT MAX(surveyId) FROM gm_survey")) _lastSurveyId = (*result)[0].GetUInt32(); TC_LOG_INFO("server.loading", ">> Loaded GM Survey count from database in %u ms", GetMSTimeDiffToNow(oldMSTime)); diff --git a/src/server/game/Tools/CharacterDatabaseCleaner.cpp b/src/server/game/Tools/CharacterDatabaseCleaner.cpp index 78bc1b799de..815212aa53d 100644 --- a/src/server/game/Tools/CharacterDatabaseCleaner.cpp +++ b/src/server/game/Tools/CharacterDatabaseCleaner.cpp @@ -147,7 +147,7 @@ bool CharacterDatabaseCleaner::TalentCheck(uint32 talent_id) void CharacterDatabaseCleaner::CleanCharacterTalent() { - CharacterDatabase.DirectPExecute("DELETE FROM character_talent WHERE spec > %u", MAX_TALENT_SPECS); + CharacterDatabase.DirectPExecute("DELETE FROM character_talent WHERE talentGroup > %u", MAX_TALENT_SPECS); CheckUnique("spell", "character_talent", &TalentCheck); } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 5f54154fab1..7cddfc3f61e 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1527,6 +1527,12 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading GameObject Addon Data..."); sObjectMgr->LoadGameObjectAddons(); // must be after LoadGameObjectTemplate() and LoadGameobjects() + TC_LOG_INFO("server.loading", "Loading GameObject Quest Items..."); + sObjectMgr->LoadGameObjectQuestItems(); + + TC_LOG_INFO("server.loading", "Loading Creature Quest Items..."); + sObjectMgr->LoadCreatureQuestItems(); + TC_LOG_INFO("server.loading", "Loading Creature Linked Respawn..."); sObjectMgr->LoadLinkedRespawn(); // must be after LoadCreatures(), LoadGameObjects() @@ -1711,6 +1717,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading faction change spell pairs..."); sObjectMgr->LoadFactionChangeSpells(); + TC_LOG_INFO("server.loading", "Loading faction change quest pairs..."); + sObjectMgr->LoadFactionChangeQuests(); + TC_LOG_INFO("server.loading", "Loading faction change item pairs..."); sObjectMgr->LoadFactionChangeItems(); diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 390be999d9d..09a41fd82b5 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -370,28 +370,13 @@ public: uint32 lowGuid = atoi((char*)guidStr); - Creature* creature = NULL; - - /* FIXME: impossible without entry - if (lowguid) - creature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(), MAKE_GUID(lowguid, HIGHGUID_UNIT)); - */ - // attempt check creature existence by DB data - if (!creature) - { - CreatureData const* data = sObjectMgr->GetCreatureData(lowGuid); - if (!data) - { - handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowGuid); - handler->SetSentErrorMessage(true); - return false; - } - } - else + CreatureData const* data = sObjectMgr->GetCreatureData(lowGuid); + if (!data) { - // obtain real GUID for DB operations - lowGuid = creature->GetDBTableGUIDLow(); + handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowGuid); + handler->SetSentErrorMessage(true); + return false; } int wait = waitStr ? atoi(waitStr) : 0; @@ -407,18 +392,6 @@ public: WorldDatabase.Execute(stmt); - if (creature && creature->GetWaypointPath()) - { - creature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE); - creature->GetMotionMaster()->Initialize(); - if (creature->IsAlive()) // dead creature will reset movement generator at respawn - { - creature->setDeathState(JUST_DIED); - creature->Respawn(true); - } - creature->SaveToDB(); - } - handler->SendSysMessage(LANG_WAYPOINT_ADDED); return true; @@ -830,34 +803,22 @@ public: lowguid = atoi(cId); - /* FIXME: impossible without entry - if (lowguid) - creature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(), MAKE_GUID(lowguid, HIGHGUID_UNIT)); - */ - // Attempting creature load from DB data - if (!creature) + CreatureData const* data = sObjectMgr->GetCreatureData(lowguid); + if (!data) { - CreatureData const* data = sObjectMgr->GetCreatureData(lowguid); - if (!data) - { - handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); - handler->SetSentErrorMessage(true); - return false; - } + handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); + handler->SetSentErrorMessage(true); + return false; + } - uint32 map_id = data->mapid; + uint32 map_id = data->mapid; - if (handler->GetSession()->GetPlayer()->GetMapId() != map_id) - { - handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid); - handler->SetSentErrorMessage(true); - return false; - } - } - else + if (handler->GetSession()->GetPlayer()->GetMapId() != map_id) { - lowguid = creature->GetDBTableGUIDLow(); + handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid); + handler->SetSentErrorMessage(true); + return false; } } else diff --git a/src/server/scripts/Commands/cs_tele.cpp b/src/server/scripts/Commands/cs_tele.cpp index e488aa0e607..8e82f9980d3 100644 --- a/src/server/scripts/Commands/cs_tele.cpp +++ b/src/server/scripts/Commands/cs_tele.cpp @@ -319,7 +319,7 @@ public: } MapEntry const* map = sMapStore.LookupEntry(tele->mapId); - if (!map || map->IsBattlegroundOrArena()) + if (!map || (map->IsBattlegroundOrArena() && (me->GetMapId() != tele->mapId || !me->IsGameMaster()))) { handler->SendSysMessage(LANG_CANNOT_TELE_TO_BG); handler->SetSentErrorMessage(true); diff --git a/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp b/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp index 1d7e1594d05..ca944f7037b 100644 --- a/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp +++ b/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp @@ -102,7 +102,7 @@ public: } } - virtual void Update(uint32 /*diff*/) // correct order goes form 1-6 + virtual void Update(uint32 /*diff*/) override // correct order goes form 1-6 { switch (State) { diff --git a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp index cae26735568..29002460b2a 100644 --- a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp +++ b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp @@ -202,7 +202,7 @@ class boss_archaedas : public CreatureScript DoMeleeAttackIfReady(); } - void JustDied (Unit* /*killer*/) + void JustDied (Unit* /*killer*/) override { instance->SetData(DATA_ANCIENT_DOOR, DONE); // open the vault door instance->SetData(DATA_MINIONS, SPECIAL); // deactivate his minions diff --git a/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp b/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp index 46e831b0f83..7bd197774bc 100644 --- a/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp +++ b/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp @@ -111,6 +111,7 @@ public: instance_zulfarrak_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); + GahzRillaEncounter = NOT_STARTED; PyramidPhase = 0; major_wave_Timer = 0; minor_wave_Timer = 0; diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp index 704fefe2678..163620230ef 100644 --- a/src/server/scripts/Kalimdor/zone_moonglade.cpp +++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp @@ -384,6 +384,8 @@ public: } PlayerGUID = player->GetGUID(); Start(true, false, PlayerGUID); + me->SetDisplayId(me->GetCreatureTemplate()->Modelid1); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } return; } diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp index ad510682e15..434028ab255 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp @@ -26,14 +26,6 @@ #include "ruby_sanctum.h" #include "Player.h" -/* ScriptData -SDName: ruby_sanctum -SDAuthors: Kaelima, Warpten -SD%Complete: 90% -SDComment: Based on Kaelima's initial work (half of it). Corporeality handling is a pure guess, we lack info. -SDCategory: Chamber of Aspects -EndScriptData */ - enum Texts { // Shared @@ -87,6 +79,8 @@ enum Spells // Living Inferno SPELL_BLAZING_AURA = 75885, + SPELL_SPAWN_LIVING_EMBERS = 75880, + SPELL_SUMMON_LIVING_EMBER = 75881, // Halion Controller SPELL_COSMETIC_FIRE_PILLAR = 76006, @@ -143,7 +137,7 @@ enum Events EVENT_CHECK_CORPOREALITY = 14, EVENT_SHADOW_PULSARS_SHOOT = 15, EVENT_TRIGGER_BERSERK = 16, - EVENT_TWILIGHT_MENDING = 17 + EVENT_TWILIGHT_MENDING = 17, }; enum Actions @@ -156,7 +150,13 @@ enum Actions ACTION_MONITOR_CORPOREALITY = 3, // Orb Carrier - ACTION_SHOOT = 4 + ACTION_SHOOT = 4, + + // Living Inferno + ACTION_SUMMON_LIVING_EMBERS = 5, + + // Meteor Flame + ACTION_SUMMON_FLAME = 6 }; enum Phases @@ -174,7 +174,8 @@ enum Misc DATA_MATERIAL_DAMAGE_TAKEN = 2, DATA_STACKS_DISPELLED = 3, DATA_FIGHT_PHASE = 4, - DATA_EVADE_METHOD = 5 + DATA_EVADE_METHOD = 5, + DATA_SPAWNED_FLAMES = 6, }; enum OrbCarrierSeats @@ -703,7 +704,7 @@ class npc_halion_controller : public CreatureScript // The IsInCombat() check is needed because that check should be false when Halion is // not engaged, while it would return true without as UpdateVictim() checks for // combat state. - if (!(_events.IsInPhase(PHASE_INTRO)) && me->IsInCombat() && !UpdateVictim()) + if (!_events.IsInPhase(PHASE_INTRO) && me->IsInCombat() && !UpdateVictim()) { EnterEvadeMode(); return; @@ -894,8 +895,6 @@ class npc_halion_controller : public CreatureScript } }; -typedef npc_halion_controller::npc_halion_controllerAI controllerAI; - class npc_orb_carrier : public CreatureScript { public: @@ -997,7 +996,7 @@ class npc_meteor_strike_initial : public CreatureScript if (!owner) return; - // Let Halion Controller count as summoner + // Let Controller count as summoner if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION_CONTROLLER))) controller->AI()->JustSummoned(me); @@ -1007,11 +1006,13 @@ class npc_meteor_strike_initial : public CreatureScript if (HalionAI* halionAI = CAST_AI(HalionAI, owner->AI())) { Position const* ownerPos = halionAI->GetMeteorStrikePosition(); + // Adjust randomness between 0 and pi. + float randomAdjustment = frand(static_cast<float>(M_PI / 14), static_cast<float>(13 * M_PI / 14)); float angle[4]; angle[0] = me->GetAngle(ownerPos); - angle[1] = me->GetAngle(ownerPos) - static_cast<float>(M_PI/2); - angle[2] = me->GetAngle(ownerPos) - static_cast<float>(-M_PI/2); - angle[3] = me->GetAngle(ownerPos) - static_cast<float>(M_PI); + angle[1] = angle[0] + randomAdjustment; + angle[2] = angle[0] + static_cast<float>(M_PI); + angle[3] = angle[2] + randomAdjustment; _meteorList.clear(); for (uint8 i = 0; i < 4; i++) @@ -1020,7 +1021,10 @@ class npc_meteor_strike_initial : public CreatureScript me->SetOrientation(angle[i]); Position newPos = me->GetNearPosition(10.0f, 0.0f); // Exact distance if (Creature* meteor = me->SummonCreature(NPC_METEOR_STRIKE_NORTH + i, newPos, TEMPSUMMON_TIMED_DESPAWN, 30000)) + { + meteor->SetOrientation(angle[i]); _meteorList.push_back(meteor); + } } } } @@ -1046,11 +1050,8 @@ class npc_meteor_strike : public CreatureScript struct npc_meteor_strikeAI : public ScriptedAI { npc_meteor_strikeAI(Creature* creature) : ScriptedAI(creature), - _instance(creature->GetInstanceScript()) + _instance(creature->GetInstanceScript()), _spawnCount(0) { - _range = 5.0f; - _spawnCount = 0; - SetCombatMovement(false); } @@ -1071,34 +1072,38 @@ class npc_meteor_strike : public CreatureScript controller->AI()->JustSummoned(me); } - void UpdateAI(uint32 diff) override + void SetData(uint32 dataType, uint32 dataCount) override { - if (_spawnCount > 5) - return; + if (dataType == DATA_SPAWNED_FLAMES) + _spawnCount += dataCount; + } - _events.Update(diff); + uint32 GetData(uint32 dataType) const override + { + if (dataType == DATA_SPAWNED_FLAMES) + return _spawnCount; + return 0; + } + void UpdateAI(uint32 diff) override + { + _events.Update(diff); if (_events.ExecuteEvent() == EVENT_SPAWN_METEOR_FLAME) { - Position pos = me->GetNearPosition( _range, 0.0f); - + Position pos = me->GetNearPosition(5.0f, 0.0f); if (Creature* flame = me->SummonCreature(NPC_METEOR_STRIKE_FLAME, pos, TEMPSUMMON_TIMED_DESPAWN, 25000)) { - if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION_CONTROLLER))) - controller->AI()->JustSummoned(flame); + flame->SetOrientation(me->GetOrientation()); - flame->CastSpell(flame, SPELL_METEOR_STRIKE_FIRE_AURA_2, true); - ++_spawnCount; + flame->AI()->SetGUID(GetGUID()); + flame->AI()->DoAction(ACTION_SUMMON_FLAME); } - _range += 5.0f; - _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 800); } } private: InstanceScript* _instance; EventMap _events; - float _range; uint8 _spawnCount; }; @@ -1108,6 +1113,67 @@ class npc_meteor_strike : public CreatureScript } }; +class npc_meteor_strike_flame : public CreatureScript +{ + public: + npc_meteor_strike_flame() : CreatureScript("npc_meteor_strike_flame") { } + + struct npc_meteor_strike_flameAI : public ScriptedAI + { + npc_meteor_strike_flameAI(Creature* creature) : ScriptedAI(creature), + _instance(creature->GetInstanceScript()) + { + SetCombatMovement(false); + } + + void SetGUID(ObjectGuid guid, int32 /*id = 0 */) override + { + _rootOwnerGuid = guid; + } + + void DoAction(int32 action) override + { + if (action != ACTION_SUMMON_FLAME || _rootOwnerGuid.IsEmpty()) + return; + + me->CastSpell(me, SPELL_METEOR_STRIKE_FIRE_AURA_2, true); + + Creature* meteorStrike = ObjectAccessor::GetCreature(*me, _rootOwnerGuid); + if (!meteorStrike || meteorStrike->AI()->GetData(DATA_SPAWNED_FLAMES) > 5) + return; + + me->SetOrientation(me->GetOrientation() + frand(static_cast<float>(-M_PI / 16), static_cast<float>(M_PI / 16))); + Position pos = me->GetNearPosition(5.0f, 0.0f); + + if (Creature* flame = me->SummonCreature(NPC_METEOR_STRIKE_FLAME, pos, TEMPSUMMON_TIMED_DESPAWN, 25000)) + { + flame->AI()->SetGUID(_rootOwnerGuid); + meteorStrike->AI()->SetData(DATA_SPAWNED_FLAMES, 1); + } + } + + void IsSummonedBy(Unit* /*summoner*/) override + { + // Let Halion Controller count as summoner. + if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION_CONTROLLER))) + controller->AI()->JustSummoned(me); + } + + void UpdateAI(uint32 /*diff*/) override { } + void EnterEvadeMode() override { } + + private: + InstanceScript* _instance; + EventMap _events; + ObjectGuid _rootOwnerGuid = ObjectGuid::Empty; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetRubySanctumAI<npc_meteor_strike_flameAI>(creature); + } +}; + class npc_combustion_consumption : public CreatureScript { public: @@ -1120,26 +1186,23 @@ class npc_combustion_consumption : public CreatureScript { SetCombatMovement(false); - switch (me->GetEntry()) + switch (creature->GetEntry()) { case NPC_COMBUSTION: _explosionSpell = SPELL_FIERY_COMBUSTION_EXPLOSION; _damageSpell = SPELL_COMBUSTION_DAMAGE_AURA; - me->SetPhaseMask(0x01, true); + creature->SetPhaseMask(IsHeroic() ? 0x21 : 0x01, true); break; case NPC_CONSUMPTION: _explosionSpell = SPELL_SOUL_CONSUMPTION_EXPLOSION; _damageSpell = SPELL_CONSUMPTION_DAMAGE_AURA; - me->SetPhaseMask(0x20, true); + creature->SetPhaseMask(IsHeroic() ? 0x21 : 0x20, true); break; default: // Should never happen _explosionSpell = 0; _damageSpell = 0; break; } - - if (IsHeroic()) - me->SetPhaseMask(0x01 | 0x20, true); } void IsSummonedBy(Unit* summoner) override @@ -1161,7 +1224,7 @@ class npc_combustion_consumption : public CreatureScript me->CastCustomSpell(SPELL_SCALE_AURA, SPELLVALUE_AURA_STACK, stackAmount, me); DoCast(me, _damageSpell); - int32 damage = 1200 + (stackAmount * 1290); // Needs more researches. + int32 damage = 1200 + (stackAmount * 1290); // Needs more research. summoner->CastCustomSpell(_explosionSpell, SPELLVALUE_BASE_POINT0, damage, summoner); } @@ -1194,6 +1257,10 @@ class npc_living_inferno : public CreatureScript me->SetInCombatWithZone(); me->CastSpell(me, SPELL_BLAZING_AURA, true); + // SMSG_SPELL_GO for the living ember stuff isn't even sent to the client - Blizzard on drugs. + if (me->GetMap()->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC) + me->CastSpell(me, SPELL_SPAWN_LIVING_EMBERS, true); + if (InstanceScript* instance = me->GetInstanceScript()) if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER))) controller->AI()->JustSummoned(me); @@ -1211,7 +1278,6 @@ class npc_living_inferno : public CreatureScript } }; -//! Need sniff data class npc_living_ember : public CreatureScript { public: @@ -1739,12 +1805,46 @@ class spell_halion_summon_exit_portals : public SpellScriptLoader } }; +class spell_halion_spawn_living_embers : public SpellScriptLoader +{ + public: + spell_halion_spawn_living_embers() : SpellScriptLoader("spell_halion_spawn_living_embers") { } + + class spell_halion_spawn_living_embers_SpellScript : public SpellScript + { + PrepareSpellScript(spell_halion_spawn_living_embers_SpellScript); + + void SelectMeteorFlames(std::list<WorldObject*>& unitList) + { + if (!unitList.empty()) + Trinity::Containers::RandomResizeList(unitList, 10); + } + + void HandleScript(SpellEffIndex /* effIndex */) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUMMON_LIVING_EMBER, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_halion_spawn_living_embers_SpellScript::SelectMeteorFlames, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_halion_spawn_living_embers_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_halion_spawn_living_embers_SpellScript(); + } +}; + void AddSC_boss_halion() { new boss_halion(); new boss_twilight_halion(); new npc_halion_controller(); + new npc_meteor_strike_flame(); new npc_meteor_strike_initial(); new npc_meteor_strike(); new npc_combustion_consumption(); @@ -1766,4 +1866,5 @@ void AddSC_boss_halion() new spell_halion_twilight_phasing(); new spell_halion_twilight_cutter(); new spell_halion_clear_debuffs(); + new spell_halion_spawn_living_embers(); } diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp index 660a639487f..fabe54303b8 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp @@ -196,7 +196,7 @@ class instance_halls_of_reflection : public InstanceMapScript } } - uint32 GetGameObjectEntry(uint32 /*guidLow*/, uint32 entry) override + uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidLow*/, uint32 entry) override { if (!_teamInInstance) { diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index 61848070170..1b823b4a452 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -317,7 +317,7 @@ class instance_icecrown_citadel : public InstanceMapScript } // Weekly quest spawn prevention - uint32 GetCreatureEntry(uint32 /*guidLow*/, CreatureData const* data) override + uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) override { uint32 entry = data->id; switch (entry) @@ -372,7 +372,7 @@ class instance_icecrown_citadel : public InstanceMapScript return entry; } - uint32 GetGameObjectEntry(uint32 /*guidLow*/, uint32 entry) override + uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidLow*/, uint32 entry) override { switch (entry) { diff --git a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp index c3f5c75e059..737a5d5c982 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp @@ -229,6 +229,21 @@ class instance_oculus : public InstanceMapScript return true; } + uint32 GetData(uint32 type) const override + { + if (type == DATA_CONSTRUCTS) + { + if (CentrifugueConstructCounter == 0) + return KILL_NO_CONSTRUCT; + else if (CentrifugueConstructCounter == 1) + return KILL_ONE_CONSTRUCT; + else if (CentrifugueConstructCounter > 1) + return KILL_MORE_CONSTRUCT; + } + + return KILL_NO_CONSTRUCT; + } + ObjectGuid GetGuidData(uint32 type) const override { switch (type) diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp index 450f97cb9b7..3ab9814b5b5 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp @@ -20,6 +20,7 @@ #include "ScriptedGossip.h" #include "SpellScript.h" #include "SpellAuraEffects.h" +#include "SpellInfo.h" #include "CombatAI.h" #include "Player.h" #include "Vehicle.h" @@ -76,6 +77,11 @@ enum Drakes SPELL_EMERALD_TOUCH_THE_NIGHTMARE = 50341, // (60 yds) - Instant - Consumes 30% of the caster's max health to inflict 25, 000 nature damage to an enemy dragon and reduce the damage it deals by 25% for 30 sec. // you do not have access to until you kill the Mage-Lord Urom SPELL_EMERALD_DREAM_FUNNEL = 50344, // (60 yds) - Channeled - Transfers 5% of the caster's max health to a friendly drake every second for 10 seconds as long as the caster channels. +/* + * All Drakes + * GPS System + */ + SPELL_GPS = 53389, // Misc POINT_LAND = 2, @@ -101,7 +107,13 @@ enum Says WHISPER_DRAKES_WELCOME = 1, WHISPER_DRAKES_ABILITIES = 2, WHISPER_DRAKES_SPECIAL = 3, - WHISPER_DRAKES_LOWHEALTH = 4 + WHISPER_DRAKES_LOWHEALTH = 4, + WHISPER_GPS_10_CONSTRUCTS = 5, + WHISPER_GPS_1_CONSTRUCT = 6, + WHISPER_GPS_VAROS = 7, + WHISPER_GPS_UROM = 8, + WHISPER_GPS_EREGOS = 9, + WHISPER_GPS_END = 10 }; class npc_verdisa_beglaristrasz_eternos : public CreatureScript @@ -250,6 +262,26 @@ class npc_ruby_emerald_amber_drake : public CreatureScript Initialize(); } + void SpellHit(Unit* /*caster*/, const SpellInfo* spell) override + { + if (Unit* creator = ObjectAccessor::GetUnit(*me, me->GetCreatorGUID())) + if (spell->Id == SPELL_GPS) + { + if (_instance->GetBossState(DATA_EREGOS) == DONE) + Talk(WHISPER_GPS_END, creator); + else if (_instance->GetBossState(DATA_UROM) == DONE) + Talk(WHISPER_GPS_EREGOS, creator); + else if (_instance->GetBossState(DATA_VAROS) == DONE) + Talk(WHISPER_GPS_UROM, creator); + else if (_instance->GetData(DATA_CONSTRUCTS) == KILL_NO_CONSTRUCT) + Talk(WHISPER_GPS_VAROS, creator); + else if (_instance->GetData(DATA_CONSTRUCTS) == KILL_ONE_CONSTRUCT) + Talk(WHISPER_GPS_1_CONSTRUCT, creator); + else if (_instance->GetData(DATA_CONSTRUCTS) == KILL_MORE_CONSTRUCT) + Talk(WHISPER_GPS_10_CONSTRUCTS, creator); + } + } + void IsSummonedBy(Unit* summoner) override { if (_instance->GetBossState(DATA_EREGOS) == IN_PROGRESS) diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.h b/src/server/scripts/Northrend/Nexus/Oculus/oculus.h index fd46f0a6bcc..08fc15c5752 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.h +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.h @@ -29,7 +29,9 @@ enum DataTypes DATA_DRAKOS = 0, DATA_VAROS = 1, DATA_UROM = 2, - DATA_EREGOS = 3 + DATA_EREGOS = 3, + // GPS System + DATA_CONSTRUCTS = 4 }; enum CreatureIds @@ -91,6 +93,13 @@ enum InstanceEvents EVENT_EREGOS_INTRO }; +enum ConstructKillState +{ + KILL_NO_CONSTRUCT = 0, + KILL_ONE_CONSTRUCT = 1, + KILL_MORE_CONSTRUCT = 2 +}; + enum Misc { POINT_MOVE_OUT = 1 diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp index 181fb49ba2e..d0e4b7be63b 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp @@ -903,17 +903,15 @@ class boss_yogg_saron : public CreatureScript DoCast(me, SPELL_KNOCK_AWAY); me->ResetLootMode(); - switch (_instance->GetData(DATA_KEEPERS_COUNT)) - { - case 0: - me->AddLootMode(LOOT_MODE_HARD_MODE_4); - case 1: - me->AddLootMode(LOOT_MODE_HARD_MODE_3); - case 2: - me->AddLootMode(LOOT_MODE_HARD_MODE_2); - case 3: - me->AddLootMode(LOOT_MODE_HARD_MODE_1); - } + uint32 keepersCount = _instance->GetData(DATA_KEEPERS_COUNT); + if (keepersCount == 0) + me->AddLootMode(LOOT_MODE_HARD_MODE_4); + if (keepersCount <= 1) + me->AddLootMode(LOOT_MODE_HARD_MODE_3); + if (keepersCount <= 2) + me->AddLootMode(LOOT_MODE_HARD_MODE_2); + if (keepersCount <= 3) + me->AddLootMode(LOOT_MODE_HARD_MODE_1); } void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override diff --git a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp index 36d2c5f8ed3..dc923e534b0 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp @@ -23,12 +23,9 @@ enum Spells { SPELL_ARCANE_VACUUM = 58694, SPELL_BLIZZARD = 58693, - H_SPELL_BLIZZARD = 59369, SPELL_MANA_DESTRUCTION = 59374, SPELL_TAIL_SWEEP = 58690, - H_SPELL_TAIL_SWEEP = 59283, SPELL_UNCONTROLLABLE_ENERGY = 58688, - H_SPELL_UNCONTROLLABLE_ENERGY = 59281, SPELL_TRANSFORM = 58668 }; @@ -48,17 +45,11 @@ class boss_cyanigosa : public CreatureScript public: boss_cyanigosa() : CreatureScript("boss_cyanigosa") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_cyanigosaAI>(creature); - } - - struct boss_cyanigosaAI : public ScriptedAI + struct boss_cyanigosaAI : public BossAI { - boss_cyanigosaAI(Creature* creature) : ScriptedAI(creature) + boss_cyanigosaAI(Creature* creature) : BossAI(creature, DATA_CYANIGOSA) { Initialize(); - instance = creature->GetInstanceScript(); } void Initialize() @@ -76,24 +67,20 @@ public: uint32 uiTailSweepTimer; uint32 uiUncontrollableEnergyTimer; - InstanceScript* instance; - void Reset() override { Initialize(); - instance->SetData(DATA_CYANIGOSA_EVENT, NOT_STARTED); + BossAI::Reset(); } - void EnterCombat(Unit* /*who*/) override + void EnterCombat(Unit* who) override { + BossAI::EnterCombat(who); Talk(SAY_AGGRO); - - instance->SetData(DATA_CYANIGOSA_EVENT, IN_PROGRESS); } void MoveInLineOfSight(Unit* /*who*/) override { } - void UpdateAI(uint32 diff) override { if (instance->GetData(DATA_REMOVE_NPC) == 1) @@ -102,13 +89,12 @@ public: instance->SetData(DATA_REMOVE_NPC, 0); } - //Return since we have no target if (!UpdateVictim()) return; if (uiArcaneVacuumTimer <= diff) { - DoCast(SPELL_ARCANE_VACUUM); + DoCastAOE(SPELL_ARCANE_VACUUM); uiArcaneVacuumTimer = 10000; } else uiArcaneVacuumTimer -= diff; @@ -121,7 +107,7 @@ public: if (uiTailSweepTimer <= diff) { - DoCast(SPELL_TAIL_SWEEP); + DoCastVictim(SPELL_TAIL_SWEEP); uiTailSweepTimer = 20000; } else uiTailSweepTimer -= diff; @@ -144,22 +130,23 @@ public: DoMeleeAttackIfReady(); } - void JustDied(Unit* /*killer*/) override + void JustDied(Unit* killer) override { + BossAI::JustDied(killer); Talk(SAY_DEATH); - - instance->SetData(DATA_CYANIGOSA_EVENT, DONE); } void KilledUnit(Unit* victim) override { - if (victim->GetTypeId() != TYPEID_PLAYER) - return; - - Talk(SAY_SLAY); + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); } }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_cyanigosaAI>(creature); + } }; class achievement_defenseless : public AchievementCriteriaScript diff --git a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp index e990b8796de..1f9fc6d7981 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp @@ -24,12 +24,11 @@ enum Spells SPELL_BLOODLUST = 54516, SPELL_BREAK_BONDS = 59463, SPELL_CHAIN_HEAL = 54481, - H_SPELL_CHAIN_HEAL = 59473, SPELL_EARTH_SHIELD = 54479, - H_SPELL_EARTH_SHIELD = 59471, SPELL_EARTH_SHOCK = 54511, SPELL_LIGHTNING_BOLT = 53044, - SPELL_STORMSTRIKE = 51876 + SPELL_STORMSTRIKE = 51876, + SPELL_WINDFURY = 54493 }; enum Yells @@ -42,6 +41,17 @@ 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: @@ -62,39 +72,50 @@ public: void Initialize() { - uiBloodlustTimer = 15000; - uiChainHealTimer = 0; - uiEarthShockTimer = urand(2000, 8000); - uiLightningBoltTimer = urand(5000, 10000); - uiEarthShieldTimer = 20000; + phase = 0; + breakBondsCd = 0; } - uint32 uiBloodlustTimer; - uint32 uiChainHealTimer; - uint32 uiEarthShockTimer; - uint32 uiLightningBoltTimer; - uint32 uiEarthShieldTimer; - - InstanceScript* instance; - void Reset() override { Initialize(); + if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); - if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) { - if (!pGuard1->IsAlive()) - pGuard1->Respawn(); + 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(); } - if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) + else { - if (!pGuard2->IsAlive()) - pGuard2->Respawn(); + 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(); + } } + + events.Reset(); + } + + void JustReachedHome() override + { + if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) + pGuard1->Respawn(); + + if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2))) + pGuard2->Respawn(); } void AttackStart(Unit* who) override @@ -111,13 +132,13 @@ public: 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); + 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); + 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); } @@ -129,124 +150,160 @@ public: Talk(SAY_AGGRO); DoCast(me, SPELL_EARTH_SHIELD); - if (GameObject* pDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_EREKEM_CELL))) - if (pDoor->GetGoState() == GO_STATE_READY) + if (GameObject* door = instance->GetGameObject(DATA_EREKEM_CELL)) + if (door->GetGoState() == GO_STATE_READY) { EnterEvadeMode(); return; } if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + + 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); } - void MoveInLineOfSight(Unit* /*who*/) override { } + 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); + } + else if (instance->GetData(DATA_WAVE_COUNT) == 12) + { + instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE); + instance->SetData(DATA_WAVE_COUNT, 13); + } + } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } void UpdateAI(uint32 diff) override { - //Return since we have no target if (!UpdateVictim()) return; - //spam stormstrike in hc mode if spawns are dead - if (IsHeroic()) - { + if (phase == 0) if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) { 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 (uiEarthShieldTimer <= diff) - { - DoCast(me, SPELL_EARTH_SHIELD); - uiEarthShieldTimer = 20000; - } else uiEarthShieldTimer -= diff; + events.Update(diff); - if (uiChainHealTimer <= diff) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (breakBondsCd <= 0) { - if (ObjectGuid TargetGUID = GetChainHealTargetGUID()) + if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1))) { - if (Creature* target = ObjectAccessor::GetCreature(*me, TargetGUID)) - DoCast(target, SPELL_CHAIN_HEAL); - - //If one of the adds is dead spawn heals faster - Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)); - Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)); - uiChainHealTimer = ((pGuard1 && !pGuard1->IsAlive()) || (pGuard2 && !pGuard2->IsAlive()) ? 3000 : 8000) + rand32() % 3000; + 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; + } + } + } } - } else uiChainHealTimer -= diff; - - if (uiBloodlustTimer <= diff) - { - DoCast(me, SPELL_BLOODLUST); - uiBloodlustTimer = urand(35000, 45000); - } else uiBloodlustTimer -= diff; - - if (uiEarthShockTimer <= diff) - { - DoCastVictim(SPELL_EARTH_SHOCK); - uiEarthShockTimer = urand(8000, 13000); - } else uiEarthShockTimer -= diff; - - if (uiLightningBoltTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_LIGHTNING_BOLT); - uiLightningBoltTimer = urand(18000, 24000); - } else uiLightningBoltTimer -= diff; - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - - if (instance->GetData(DATA_WAVE_COUNT) == 6) - { - instance->SetData(DATA_1ST_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 7); } - else if (instance->GetData(DATA_WAVE_COUNT) == 12) + else + breakBondsCd -= diff; + + switch (uint32 eventId = events.ExecuteEvent()) { - instance->SetData(DATA_2ND_BOSS_EVENT, DONE); - instance->SetData(DATA_WAVE_COUNT, 13); + case EVENT_EARTH_SHIELD: + if (Unit* ally = DoSelectLowestHpFriendly(30.0f)) + DoCast(ally, SPELL_EARTH_SHIELD); + events.ScheduleEvent(EVENT_EARTH_SHIELD, 20000); + break; + case EVENT_BLOODLUST: + DoCast(SPELL_BLOODLUST); + events.ScheduleEvent(EVENT_BLOODLUST, urand(35000, 45000)); + break; + case EVENT_LIGHTNING_BOLT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_LIGHTNING_BOLT); + events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 2500); + break; + case EVENT_CHAIN_HEAL: + if (Unit* ally = DoSelectLowestHpFriendly(40.0f)) + DoCast(ally, SPELL_CHAIN_HEAL); + { + 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)); + } + 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; } - } - - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() != TYPEID_PLAYER) - return; - Talk(SAY_SLAY); + DoMeleeAttackIfReady(); } - ObjectGuid GetChainHealTargetGUID() - { - if (HealthBelowPct(85)) - return me->GetGUID(); - - Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)); - if (pGuard1 && pGuard1->IsAlive() && !pGuard1->HealthAbovePct(75)) - return pGuard1->GetGUID(); - - Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)); - if (pGuard2 && pGuard2->IsAlive() && !pGuard2->HealthAbovePct(75)) - return pGuard2->GetGUID(); - - return ObjectGuid::Empty; - } + private: + EventMap events; + InstanceScript* instance; + uint8 phase; + int32 breakBondsCd; }; - }; enum GuardSpells @@ -290,6 +347,9 @@ public: void Reset() override { Initialize(); + + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); } void AttackStart(Unit* who) override @@ -308,7 +368,6 @@ public: void MoveInLineOfSight(Unit* /*who*/) override { } - void UpdateAI(uint32 diff) override { if (!UpdateVictim()) @@ -335,7 +394,6 @@ public: } else uiGushingWoundTimer -= diff; } }; - }; 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 48fe8049d19..cfdacb10896 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp @@ -23,19 +23,20 @@ enum Spells { SPELL_DRAINED = 59820, SPELL_FRENZY = 54312, - SPELL_FRENZY_H = 59522, SPELL_PROTECTIVE_BUBBLE = 54306, SPELL_WATER_BLAST = 54237, - SPELL_WATER_BLAST_H = 59520, SPELL_WATER_BOLT_VOLLEY = 54241, - SPELL_WATER_BOLT_VOLLEY_H = 59521, SPELL_SPLASH = 59516, - SPELL_WATER_GLOBULE = 54268 + SPELL_BURST = 54379, + SPELL_WATER_GLOBULE = 54268, + SPELL_MERGE = 54269, + SPELL_WATER_GLOBULE_VISUAL = 54260 }; enum IchoronCreatures { NPC_ICHOR_GLOBULE = 29321, + NPC_ICHORON_SUMMON_TARGET = 29326 }; enum Yells @@ -51,75 +52,83 @@ enum Yells enum Actions { - ACTION_WATER_ELEMENT_HIT = 1, - ACTION_WATER_ELEMENT_KILLED = 2, + ACTION_WATER_ELEMENT_HIT = 1 }; -/// @todo get those positions from spawn of creature 29326 -#define MAX_SPAWN_LOC 5 -static Position SpawnLoc[MAX_SPAWN_LOC]= +enum IchoronEvents { - {1840.64f, 795.407f, 44.079f, 1.676f}, - {1886.24f, 757.733f, 47.750f, 5.201f}, - {1877.91f, 845.915f, 43.417f, 3.560f}, - {1918.97f, 850.645f, 47.225f, 4.136f}, - {1935.50f, 796.224f, 52.492f, 4.224f}, + EVENT_WATER_BLAST = 1, + EVENT_WATER_BOLT_VOLLEY +}; + +enum GlobuleEvents +{ + EVENT_GLOBULE_MOVE = 1 }; 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") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_ichoronAI>(creature); - } - struct boss_ichoronAI : public ScriptedAI { boss_ichoronAI(Creature* creature) : ScriptedAI(creature), m_waterElements(creature) { Initialize(); - instance = creature->GetInstanceScript(); + instance = creature->GetInstanceScript(); } void Initialize() { bIsExploded = false; bIsFrenzy = false; + bIsDrained = false; dehydration = true; - uiBubbleCheckerTimer = 1000; - uiWaterBoltVolleyTimer = urand(10000, 15000); + drainedTimer = 50; + burstTimer = 15000; } - bool bIsExploded; - bool bIsFrenzy; - bool dehydration; - - uint32 uiBubbleCheckerTimer; - uint32 uiWaterBoltVolleyTimer; - - InstanceScript* instance; - - SummonList m_waterElements; - void Reset() override { Initialize(); + events.Reset(); me->SetVisible(true); DespawnWaterElements(); if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); } void EnterCombat(Unit* /*who*/) override @@ -128,16 +137,20 @@ public: DoCast(me, SPELL_PROTECTIVE_BUBBLE); - if (GameObject* pDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_ICHORON_CELL))) - if (pDoor->GetGoState() == GO_STATE_READY) + if (GameObject* door = instance->GetGameObject(DATA_ICHORON_CELL)) + if (door->GetGoState() == GO_STATE_READY) { EnterEvadeMode(); return; } + if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + + events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, urand(10000, 15000)); + events.ScheduleEvent(EVENT_WATER_BLAST, urand(6000, 9000)); } void AttackStart(Unit* who) override @@ -162,18 +175,14 @@ public: switch (param) { case ACTION_WATER_ELEMENT_HIT: - me->ModifyHealth(int32(me->CountPctFromMaxHealth(1))); - + { if (bIsExploded) DoExplodeCompleted(); + me->SetHealth(me->GetHealth() + me->CountPctFromMaxHealth(3)); dehydration = false; - break; - case ACTION_WATER_ELEMENT_KILLED: - uint32 damage = me->CountPctFromMaxHealth(3); - me->ModifyHealth(-int32(damage)); - me->LowerPlayerDamageReq(damage); - break; + } + break; } } @@ -187,6 +196,7 @@ public: void DoExplodeCompleted() { bIsExploded = false; + bIsDrained = false; if (!HealthBelowPct(25)) { @@ -206,75 +216,24 @@ public: return 0; } - void MoveInLineOfSight(Unit* /*who*/) override { } - - - void UpdateAI(uint32 uiDiff) override + void MoveInLineOfSight(Unit* who) override { - if (!UpdateVictim()) + if (!who->ToCreature()) return; - if (!bIsFrenzy && HealthBelowPct(25) && !bIsExploded) - { - Talk(SAY_ENRAGE); - DoCast(me, SPELL_FRENZY, true); - bIsFrenzy = true; - } - - if (!bIsFrenzy) - { - if (uiBubbleCheckerTimer <= uiDiff) - { - if (!bIsExploded) - { - if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE)) - { - Talk(SAY_SHATTER); - DoCast(me, SPELL_WATER_BLAST); - DoCast(me, SPELL_DRAINED); - bIsExploded = true; - me->AttackStop(); - me->SetVisible(false); - for (uint8 i = 0; i < 10; i++) - { - int tmp = urand(0, MAX_SPAWN_LOC-1); - me->SummonCreature(NPC_ICHOR_GLOBULE, SpawnLoc[tmp], TEMPSUMMON_CORPSE_DESPAWN); - } - } - } - else - { - bool bIsWaterElementsAlive = false; - if (!m_waterElements.empty()) - { - for (SummonList::const_iterator itr = m_waterElements.begin(); itr != m_waterElements.end(); ++itr) - if (Creature* temp = ObjectAccessor::GetCreature(*me, *itr)) - if (temp->IsAlive()) - { - bIsWaterElementsAlive = true; - break; - } - } + if (who->GetEntry() != NPC_ICHOR_GLOBULE) + return; - if (!bIsWaterElementsAlive) - DoExplodeCompleted(); - } - uiBubbleCheckerTimer = 1000; - } - else uiBubbleCheckerTimer -= uiDiff; - } + if (!me->IsWithinDist(who, 4.0f, false)) + return; - if (!bIsExploded) - { - if (uiWaterBoltVolleyTimer <= uiDiff) - { - DoCast(me, SPELL_WATER_BOLT_VOLLEY); - uiWaterBoltVolleyTimer = urand(10000, 15000); - } - else uiWaterBoltVolleyTimer -= uiDiff; + if (who->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + return; - DoMeleeAttackIfReady(); - } + who->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + who->CastSpell(who, SPELL_MERGE); + DoAction(ACTION_WATER_ELEMENT_HIT); + who->ToCreature()->DespawnOrUnsummon(1000); } void JustDied(Unit* /*killer*/) override @@ -291,45 +250,187 @@ public: if (instance->GetData(DATA_WAVE_COUNT) == 6) { - instance->SetData(DATA_1ST_BOSS_EVENT, DONE); + instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); instance->SetData(DATA_WAVE_COUNT, 7); } else if (instance->GetData(DATA_WAVE_COUNT) == 12) { - instance->SetData(DATA_2ND_BOSS_EVENT, DONE); + instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE); instance->SetData(DATA_WAVE_COUNT, 13); } } void JustSummoned(Creature* summoned) override { - if (summoned) - { - summoned->SetSpeed(MOVE_RUN, 0.3f); - summoned->GetMotionMaster()->MoveFollow(me, 0, 0); - m_waterElements.Summon(summoned); - instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); - } + summoned->SetSpeed(MOVE_RUN, 0.3f); + m_waterElements.Summon(summoned); + + instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); } void SummonedCreatureDespawn(Creature* summoned) override { - if (summoned) + m_waterElements.Despawn(summoned); + + if (m_waterElements.empty() && bIsExploded) { - m_waterElements.Despawn(summoned); - instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID()); + me->RemoveAllAuras(); + DoExplodeCompleted(); } + + instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID()); } void KilledUnit(Unit* victim) override { - if (victim->GetTypeId() != TYPEID_PLAYER) + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) return; - Talk(SAY_SLAY); + if (!bIsFrenzy && HealthBelowPct(25) && !bIsExploded) + { + Talk(SAY_ENRAGE); + DoCast(me, SPELL_FRENZY, true); + bIsFrenzy = true; + } + + if (!bIsFrenzy) + { + if (!bIsExploded) + { + if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE)) + { + bIsExploded = true; + Talk(SAY_SHATTER); + DoCast(SPELL_BURST); + me->RemoveAllAuras(); + burstTimer = 15000; + + std::list<Creature*> summonTargets; + GetCreatureListWithEntryInGrid(summonTargets, me, NPC_ICHORON_SUMMON_TARGET, 200.0f); + std::list<Creature*>::iterator itr = summonTargets.begin(); + + 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 (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + events.Update(diff); + + switch (uint32 eventId = 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; + } + + 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; + + events.Update(diff); + + switch (uint32 eventId = 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; + } + + DoMeleeAttackIfReady(); + } } + + 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); + } }; class npc_ichor_globule : public CreatureScript @@ -337,11 +438,6 @@ class npc_ichor_globule : public CreatureScript public: npc_ichor_globule() : CreatureScript("npc_ichor_globule") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_ichor_globuleAI>(creature); - } - struct npc_ichor_globuleAI : public ScriptedAI { npc_ichor_globuleAI(Creature* creature) : ScriptedAI(creature) @@ -352,51 +448,74 @@ public: void Initialize() { - uiRangeCheck_Timer = 1000; + pathId = 0; } - InstanceScript* instance; - - uint32 uiRangeCheck_Timer; - void Reset() override { Initialize(); - DoCast(me, SPELL_WATER_GLOBULE); + events.Reset(); + DoCast(SPELL_WATER_GLOBULE); + me->SetReactState(REACT_PASSIVE); } - void AttackStart(Unit* /*who*/) override + void SetData(uint32 id, uint32 data) override { - return; + if (id == DATA_GLOBULE_PATH) + { + pathId = data; + me->GetMotionMaster()->MovePoint(0, globulePaths[pathId]); + } } - void UpdateAI(uint32 uiDiff) override + void MovementInform(uint32 type, uint32 id) override { - if (uiRangeCheck_Timer < uiDiff) + if (type != POINT_MOTION_TYPE) + return; + + switch (id) { - if (Creature* pIchoron = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_ICHORON))) - { - if (me->IsWithinDist(pIchoron, 2.0f, false)) - { - if (pIchoron->AI()) - pIchoron->AI()->DoAction(ACTION_WATER_ELEMENT_HIT); - me->DespawnOrUnsummon(); - } - } - uiRangeCheck_Timer = 1000; + 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; } - else uiRangeCheck_Timer -= uiDiff; } - void JustDied(Unit* /*killer*/) override + // 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; + + if (actualHp <= 0) + DoCast(SPELL_SPLASH); + } + + void UpdateAI(uint32 diff) override { - DoCast(me, SPELL_SPLASH); - if (Creature* pIchoron = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_ICHORON))) - if (pIchoron->AI()) - pIchoron->AI()->DoAction(ACTION_WATER_ELEMENT_KILLED); + events.Update(diff); + + if (events.ExecuteEvent() == EVENT_GLOBULE_MOVE) + me->GetMotionMaster()->MovePoint(1, globulePaths[pathId + 1]); } + + private: + InstanceScript* instance; + EventMap events; + uint8 pathId; }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_ichor_globuleAI>(creature); + } }; class achievement_dehydration : public AchievementCriteriaScript diff --git a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp index c3df7b71b83..8dc0e32fb31 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp @@ -21,13 +21,18 @@ enum Spells { - SPELL_CAUTERIZING_FLAMES = 59466, //Only in heroic - SPELL_FIREBOLT = 54235, - H_SPELL_FIREBOLT = 59468, - SPELL_FLAME_BREATH = 54282, - H_SPELL_FLAME_BREATH = 59469, - SPELL_LAVA_BURN = 54249, - H_SPELL_LAVA_BURN = 59594 + SPELL_CAUTERIZING_FLAMES = 59466, // Only in heroic + SPELL_FIREBOLT = 54235, + SPELL_FLAME_BREATH = 54282, + SPELL_LAVA_BURN = 54249 +}; + +enum LavanthorEvents +{ + EVENT_CAUTERIZING_FLAMES = 1, + EVENT_FIREBOLT, + EVENT_FLAME_BREATH, + EVENT_LAVA_BURN }; class boss_lavanthor : public CreatureScript @@ -44,46 +49,37 @@ public: { boss_lavanthorAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); instance = creature->GetInstanceScript(); } - void Initialize() - { - uiFireboltTimer = 1000; - uiFlameBreathTimer = 5000; - uiLavaBurnTimer = 10000; - uiCauterizingFlamesTimer = 3000; - } - - uint32 uiFireboltTimer; - uint32 uiFlameBreathTimer; - uint32 uiLavaBurnTimer; - uint32 uiCauterizingFlamesTimer; - - InstanceScript* instance; - void Reset() override { - Initialize(); if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); + + events.Reset(); } void EnterCombat(Unit* /*who*/) override { - if (GameObject* pDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_LAVANTHOR_CELL))) - if (pDoor->GetGoState() == GO_STATE_READY) - { - EnterEvadeMode(); - return; - } - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, IN_PROGRESS); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + 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 AttackStart(Unit* who) override @@ -100,40 +96,38 @@ public: } } - void MoveInLineOfSight(Unit* /*who*/) override { } - - void UpdateAI(uint32 diff) override { - //Return since we have no target if (!UpdateVictim()) return; - if (uiFireboltTimer <= diff) - { - DoCastVictim(SPELL_FIREBOLT); - uiFireboltTimer = urand(5000, 13000); - } else uiFireboltTimer -= diff; + events.Update(diff); - if (uiFlameBreathTimer <= diff) - { - DoCastVictim(SPELL_FLAME_BREATH); - uiFlameBreathTimer = urand(10000, 15000); - } else uiFlameBreathTimer -= diff; - - if (uiLavaBurnTimer <= diff) - { - DoCastVictim(SPELL_LAVA_BURN); - uiLavaBurnTimer = urand(15000, 23000); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (IsHeroic()) + switch (uint32 eventId = events.ExecuteEvent()) { - if (uiCauterizingFlamesTimer <= diff) - { - DoCastVictim(SPELL_CAUTERIZING_FLAMES); - uiCauterizingFlamesTimer = urand(10000, 16000); - } else uiCauterizingFlamesTimer -= diff; + case EVENT_FIREBOLT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + 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)) + 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; } DoMeleeAttackIfReady(); @@ -143,17 +137,20 @@ public: { if (instance->GetData(DATA_WAVE_COUNT) == 6) { - instance->SetData(DATA_1ST_BOSS_EVENT, DONE); + instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); instance->SetData(DATA_WAVE_COUNT, 7); } else if (instance->GetData(DATA_WAVE_COUNT) == 12) { - instance->SetData(DATA_2ND_BOSS_EVENT, DONE); + instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE); instance->SetData(DATA_WAVE_COUNT, 13); } } - }; + 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 9a6422dec32..f9e223d3bab 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp @@ -17,13 +17,27 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" #include "violet_hold.h" -//Spells enum Spells { SPELL_CORROSIVE_SALIVA = 54527, - SPELL_OPTIC_LINK = 54396 + SPELL_OPTIC_LINK = 54396, + SPELL_RAY_OF_PAIN = 54438, // NYI missing spelldifficulty + SPELL_RAY_OF_SUFFERING = 54442, // NYI missing spelldifficulty + + // Visual + SPELL_OPTIC_LINK_LEVEL_1 = 54393, + SPELL_OPTIC_LINK_LEVEL_2 = 54394, + SPELL_OPTIC_LINK_LEVEL_3 = 54395 +}; + +enum MoraggEvents +{ + EVENT_CORROSIVE_SALIVA = 1, + EVENT_OPTIC_LINK }; class boss_moragg : public CreatureScript @@ -31,52 +45,43 @@ class boss_moragg : public CreatureScript public: boss_moragg() : CreatureScript("boss_moragg") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_moraggAI>(creature); - } - struct boss_moraggAI : public ScriptedAI { boss_moraggAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); instance = creature->GetInstanceScript(); } - void Initialize() - { - uiOpticLinkTimer = 10000; - uiCorrosiveSalivaTimer = 5000; - } - - uint32 uiOpticLinkTimer; - uint32 uiCorrosiveSalivaTimer; - - InstanceScript* instance; - void Reset() override { - Initialize(); + events.Reset(); if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); } void EnterCombat(Unit* /*who*/) override { - if (GameObject* pDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_MORAGG_CELL))) - if (pDoor->GetGoState() == GO_STATE_READY) + if (GameObject* door = instance->GetGameObject(DATA_MORAGG_CELL)) + if (door->GetGoState() == GO_STATE_READY) { EnterEvadeMode(); return; } + if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS); else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS); + 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 @@ -93,48 +98,202 @@ public: } } - void MoveInLineOfSight(Unit* /*who*/) override { } - - void UpdateAI(uint32 diff) override { - //Return since we have no target if (!UpdateVictim()) return; - if (uiOpticLinkTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_OPTIC_LINK); - uiOpticLinkTimer = 15000; - } else uiOpticLinkTimer -= diff; + events.Update(diff); - if (uiCorrosiveSalivaTimer <= diff) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (uint32 eventId = events.ExecuteEvent()) { - DoCastVictim(SPELL_CORROSIVE_SALIVA); - uiCorrosiveSalivaTimer = 10000; - } else uiCorrosiveSalivaTimer -= diff; + 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; + } DoMeleeAttackIfReady(); } + void JustDied(Unit* /*killer*/) override { if (instance->GetData(DATA_WAVE_COUNT) == 6) { - instance->SetData(DATA_1ST_BOSS_EVENT, DONE); + instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); instance->SetData(DATA_WAVE_COUNT, 7); } else if (instance->GetData(DATA_WAVE_COUNT) == 12) { - instance->SetData(DATA_2ND_BOSS_EVENT, DONE); + instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE); instance->SetData(DATA_WAVE_COUNT, 13); } } + + 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") { } + + class spell_moragg_ray_of_suffering_AuraScript : public AuraScript + { + PrepareAuraScript(spell_moragg_ray_of_suffering_AuraScript); + + void OnPeriodic(AuraEffect const* aurEff) + { + PreventDefaultAction(); + std::list<HostileReference*> players = GetTarget()->getThreatManager().getThreatList(); + if (!players.empty()) + { + std::list<HostileReference*>::iterator itr = players.begin(); + std::advance(itr, urand(0, players.size() - 1)); + + uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; + GetTarget()->CastCustomSpell(triggerSpell, SPELLVALUE_MAX_TARGETS, 1, (*itr)->getTarget(), TRIGGERED_FULL_MASK, NULL, aurEff); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_ray_of_suffering_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_moragg_ray_of_suffering_AuraScript(); + } +}; + +class spell_moragg_ray_of_pain : public SpellScriptLoader +{ +public: + spell_moragg_ray_of_pain() : SpellScriptLoader("spell_moragg_ray_of_pain") { } + + class spell_moragg_ray_of_pain_AuraScript : public AuraScript + { + PrepareAuraScript(spell_moragg_ray_of_pain_AuraScript); + + void OnPeriodic(AuraEffect const* aurEff) + { + PreventDefaultAction(); + std::list<HostileReference*> players = GetTarget()->getThreatManager().getThreatList(); + if (!players.empty()) + { + std::list<HostileReference*>::iterator itr = players.begin(); + std::advance(itr, urand(0, players.size() - 1)); + + uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; + GetTarget()->CastCustomSpell(triggerSpell, SPELLVALUE_MAX_TARGETS, 1, (*itr)->getTarget(), TRIGGERED_FULL_MASK, NULL, aurEff); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_ray_of_pain_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_moragg_ray_of_pain_AuraScript(); + } +}; + +class spell_moragg_optic_link : public SpellScriptLoader +{ +public: + spell_moragg_optic_link() : SpellScriptLoader("spell_moragg_optic_link") { } + + class spell_moragg_optic_link_AuraScript : public AuraScript + { + PrepareAuraScript(spell_moragg_optic_link_AuraScript); + + void OnPeriodic(AuraEffect const* aurEff) + { + switch (aurEff->GetTickNumber()) // Different visual based on tick + { + 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; + } + } + + void OnUpdate(AuraEffect* aurEff) + { + switch (aurEff->GetTickNumber()) + { + case 1: + aurEff->SetAmount(aurEff->GetAmount() + 250); // base amount is 500 + break; + case 4: + aurEff->SetAmount(aurEff->GetAmount() * 2); // goes to 1500 + break; + case 8: + aurEff->SetAmount(aurEff->GetAmount() * 2); // goes to 3000 + break; + default: + break; + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_optic_link_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_moragg_optic_link_AuraScript::OnUpdate, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_moragg_optic_link_AuraScript(); + } }; void AddSC_boss_moragg() { new boss_moragg(); + new spell_moragg_ray_of_suffering(); + new spell_moragg_ray_of_pain(); + 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 833b06cfbff..4fb7646558d 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp @@ -17,24 +17,24 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" -#include "violet_hold.h" +#include "SpellInfo.h" +#include "SpellScript.h" #include "Player.h" +#include "violet_hold.h" enum Spells { SPELL_ARCANE_BARRAGE_VOLLEY = 54202, - SPELL_ARCANE_BARRAGE_VOLLEY_H = 59483, SPELL_ARCANE_BUFFET = 54226, - SPELL_ARCANE_BUFFET_H = 59485, SPELL_SUMMON_ETHEREAL_SPHERE_1 = 54102, - SPELL_SUMMON_ETHEREAL_SPHERE_2 = 54137, + SPELL_SUMMON_ETHEREAL_SPHERE_2 = 61337, SPELL_SUMMON_ETHEREAL_SPHERE_3 = 54138 }; enum NPCs { NPC_ETHEREAL_SPHERE = 29271, - //NPC_ETHEREAL_SPHERE2 = 32582, // heroic only? + NPC_ETHEREAL_SPHERE2 = 32582 }; enum CreatureSpells @@ -42,7 +42,8 @@ enum CreatureSpells SPELL_ARCANE_POWER = 54160, H_SPELL_ARCANE_POWER = 59474, SPELL_SUMMON_PLAYERS = 54164, - SPELL_POWER_BALL_VISUAL = 54141 + SPELL_POWER_BALL_VISUAL = 54141, + SPELL_POWER_BALL_DAMAGE_TRIGGER = 54207 }; enum Yells @@ -56,52 +57,50 @@ enum Yells 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 +}; + +enum SphereActions +{ + ACTION_SUMMON = 1, +}; + class boss_xevozz : public CreatureScript { public: boss_xevozz() : CreatureScript("boss_xevozz") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_xevozzAI>(creature); - } - struct boss_xevozzAI : public ScriptedAI { boss_xevozzAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); - instance = creature->GetInstanceScript(); + instance = creature->GetInstanceScript(); } - void Initialize() - { - uiSummonEtherealSphere_Timer = urand(10000, 12000); - uiArcaneBarrageVolley_Timer = urand(20000, 22000); - uiArcaneBuffet_Timer = uiSummonEtherealSphere_Timer + urand(5000, 6000); - } - - InstanceScript* instance; - - uint32 uiSummonEtherealSphere_Timer; - uint32 uiArcaneBarrageVolley_Timer; - uint32 uiArcaneBuffet_Timer; - void Reset() override { if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED); else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); - Initialize(); DespawnSphere(); + events.Reset(); } void DespawnSphere() { std::list<Creature*> assistList; GetCreatureListWithEntryInGrid(assistList, me, NPC_ETHEREAL_SPHERE, 150.0f); + GetCreatureListWithEntryInGrid(assistList, me, NPC_ETHEREAL_SPHERE2, 150.0f); if (assistList.empty()) return; @@ -116,11 +115,7 @@ public: void JustSummoned(Creature* summoned) override { summoned->SetSpeed(MOVE_RUN, 0.5f); - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - { - summoned->AddThreat(target, 0.00f); - summoned->AI()->AttackStart(target); - } + summoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f); } void AttackStart(Unit* who) override @@ -139,58 +134,23 @@ public: void EnterCombat(Unit* /*who*/) override { - Talk(SAY_AGGRO); - if (GameObject* pDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_XEVOZZ_CELL))) - if (pDoor->GetGoState() == GO_STATE_READY) + if (GameObject* door = instance->GetGameObject(DATA_XEVOZZ_CELL)) + if (door->GetGoState() == GO_STATE_READY) { EnterEvadeMode(); return; } - if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, IN_PROGRESS); - else if (instance->GetData(DATA_WAVE_COUNT) == 12) - instance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS); - } - - void MoveInLineOfSight(Unit* /*who*/) override { } + Talk(SAY_AGGRO); - void UpdateAI(uint32 uiDiff) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - if (uiArcaneBarrageVolley_Timer < uiDiff) - { - DoCast(me, SPELL_ARCANE_BARRAGE_VOLLEY); - uiArcaneBarrageVolley_Timer = urand(20000, 22000); - } - else uiArcaneBarrageVolley_Timer -= uiDiff; - - if (uiArcaneBuffet_Timer) - { - if (uiArcaneBuffet_Timer < uiDiff) - { - DoCastVictim(SPELL_ARCANE_BUFFET); - uiArcaneBuffet_Timer = 0; - } - else uiArcaneBuffet_Timer -= uiDiff; - } - - if (uiSummonEtherealSphere_Timer < uiDiff) - { - Talk(SAY_SPAWN); - DoCast(me, SPELL_SUMMON_ETHEREAL_SPHERE_1); - if (IsHeroic()) // extra one for heroic - me->SummonCreature(NPC_ETHEREAL_SPHERE, me->GetPositionX() - 5 + rand32() % 10, me->GetPositionY() - 5 + rand32() % 10, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 40000); - - uiSummonEtherealSphere_Timer = urand(45000, 47000); - uiArcaneBuffet_Timer = urand(5000, 6000); - } - else uiSummonEtherealSphere_Timer -= uiDiff; + 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); - DoMeleeAttackIfReady(); + 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 @@ -201,24 +161,86 @@ public: if (instance->GetData(DATA_WAVE_COUNT) == 6) { - instance->SetData(DATA_1ST_BOSS_EVENT, DONE); + instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); instance->SetData(DATA_WAVE_COUNT, 7); } else if (instance->GetData(DATA_WAVE_COUNT) == 12) { - instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED); instance->SetData(DATA_WAVE_COUNT, 13); } } + void KilledUnit(Unit* victim) override { - if (victim->GetTypeId() != TYPEID_PLAYER) + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void SpellHit(Unit* who, const SpellInfo* spell) override + { + if (!who->ToCreature()) + return; + + if ((spell->Id == SPELL_ARCANE_POWER) || (spell->Id == H_SPELL_ARCANE_POWER)) + Talk(SAY_SUMMON_ENERGY); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - Talk(SAY_SLAY); + switch (uint32 eventId = 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: + if (Creature* sphere = me->FindNearestCreature(NPC_ETHEREAL_SPHERE, 150.0f)) + sphere->GetAI()->DoAction(ACTION_SUMMON); + else if (Creature* sphere = me->FindNearestCreature(NPC_ETHEREAL_SPHERE2, 150.0f)) + sphere->GetAI()->DoAction(ACTION_SUMMON); + break; + default: + break; + } + + DoMeleeAttackIfReady(); } + + private: + InstanceScript* instance; + EventMap events; }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_xevozzAI>(creature); + } }; class npc_ethereal_sphere : public CreatureScript @@ -226,83 +248,149 @@ class npc_ethereal_sphere : public CreatureScript public: npc_ethereal_sphere() : CreatureScript("npc_ethereal_sphere") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_ethereal_sphereAI>(creature); - } - struct npc_ethereal_sphereAI : public ScriptedAI { npc_ethereal_sphereAI(Creature* creature) : ScriptedAI(creature) { Initialize(); - instance = creature->GetInstanceScript(); + instance = creature->GetInstanceScript(); } void Initialize() { - uiSummonPlayers_Timer = urand(33000, 35000); - uiRangeCheck_Timer = 1000; + arcanePower = false; } - InstanceScript* instance; - - uint32 uiSummonPlayers_Timer; - uint32 uiRangeCheck_Timer; - 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); } - void UpdateAI(uint32 uiDiff) override + void DoAction(int32 action) override { - //Return since we have no target - if (!UpdateVictim()) - return; + if (action == ACTION_SUMMON) + DoCast(SPELL_SUMMON_PLAYERS); + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); - if (!me->HasAura(SPELL_POWER_BALL_VISUAL)) - DoCast(me, SPELL_POWER_BALL_VISUAL); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (uiRangeCheck_Timer < uiDiff) + switch (uint32 eventId = events.ExecuteEvent()) { - if (Creature* pXevozz = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_XEVOZZ))) - { - float fDistance = me->GetDistance2d(pXevozz); - if (fDistance <= 3) - DoCast(pXevozz, SPELL_ARCANE_POWER); - else - DoCast(me, 35845); //Is it blizzlike? - } - uiRangeCheck_Timer = 1000; + case EVENT_RANGE_CHECK: + if (Creature* xevozz = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_XEVOZZ))) + { + if (me->IsWithinDist(xevozz, 3.0f) && !arcanePower) + { + DoCast(SPELL_ARCANE_POWER); + arcanePower = true; + events.ScheduleEvent(EVENT_DESPAWN_SPHERE, 8000); + } + } + events.ScheduleEvent(EVENT_RANGE_CHECK, 1000); + break; + case EVENT_DESPAWN_SPHERE: + me->DespawnOrUnsummon(); + break; } - else uiRangeCheck_Timer -= uiDiff; + } + + private: + InstanceScript* instance; + EventMap events; + bool arcanePower; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_ethereal_sphereAI>(creature); + } +}; - if (uiSummonPlayers_Timer < uiDiff) +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); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + + if (target) { - DoCast(me, SPELL_SUMMON_PLAYERS); // not working right + Position pos = GetOriginalCaster()->GetPosition(); - Map* map = me->GetMap(); - if (map && map->IsDungeon()) - { - Map::PlayerList const &PlayerList = map->GetPlayers(); + target->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); + } + } - if (!PlayerList.isEmpty()) - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (i->GetSource()->IsAlive()) - DoTeleportPlayer(i->GetSource(), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), i->GetSource()->GetOrientation()); - } + 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; - uiSummonPlayers_Timer = urand(33000, 35000); + while (distance < 20.0f) + { + pos = caster->GetRandomNearPosition(60.0f); + distance = caster->GetDistance(pos); } - else uiSummonPlayers_Timer -= uiDiff; + + target.Relocate(pos); + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_xevozz_summon_ethereal_sphere_SpellScript::HandleScript, EFFECT_0, TARGET_DEST_DB); } }; + SpellScript* GetSpellScript() const override + { + return new spell_xevozz_summon_ethereal_sphere_SpellScript(); + } }; 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 90fd936e853..02e479a22f4 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp @@ -22,15 +22,9 @@ enum Spells { SPELL_SHROUD_OF_DARKNESS = 54524, - H_SPELL_SHROUD_OF_DARKNESS = 59745, SPELL_SUMMON_VOID_SENTRY = 54369, SPELL_VOID_SHIFT = 54361, - H_SPELL_VOID_SHIFT = 59743, -}; - -enum Creatures -{ - NPC_VOID_SENTRY = 29364 + SPELL_VOID_SHIFTED = 54343, }; enum Yells @@ -48,19 +42,21 @@ enum Misc 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") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_zuramatAI>(creature); - } - struct boss_zuramatAI : public ScriptedAI { - boss_zuramatAI(Creature* creature) : ScriptedAI(creature) + boss_zuramatAI(Creature* creature) : ScriptedAI(creature), sentries(me) { Initialize(); instance = creature->GetInstanceScript(); @@ -68,18 +64,18 @@ public: void Initialize() { - SpellShroudOfDarknessTimer = 22000; - SpellVoidShiftTimer = 15000; - SpellSummonVoidTimer = 12000; voidDance = true; } - InstanceScript* instance; - - uint32 SpellVoidShiftTimer; - uint32 SpellSummonVoidTimer; - uint32 SpellShroudOfDarknessTimer; - bool voidDance; + 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 { @@ -89,6 +85,8 @@ public: instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED); Initialize(); + events.Reset(); + DespawnSentries(); } void AttackStart(Unit* who) override @@ -107,48 +105,29 @@ public: void EnterCombat(Unit* /*who*/) override { - Talk(SAY_AGGRO); - if (GameObject* pDoor = instance->instance->GetGameObject(instance->GetGuidData(DATA_ZURAMAT_CELL))) - if (pDoor->GetGoState() == GO_STATE_READY) + if (GameObject* door = instance->GetGameObject(DATA_ZURAMAT_CELL)) + if (door->GetGoState() == GO_STATE_READY) { EnterEvadeMode(); return; } + + Talk(SAY_AGGRO); + if (instance->GetData(DATA_WAVE_COUNT) == 6) - instance->SetData(DATA_1ST_BOSS_EVENT, IN_PROGRESS); + 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 MoveInLineOfSight(Unit* /*who*/) override { } + me->SetInCombatWithZone(); + events.ScheduleEvent(EVENT_SHROUD_OF_DARKNESS, urand(18000, 20000)); + events.ScheduleEvent(EVENT_VOID_SHIFT, 9000); + events.ScheduleEvent(EVENT_SUMMON_VOID, 4000); + } - void UpdateAI(uint32 diff) override + void JustSummoned(Creature* summon) override { - //Return since we have no target - if (!UpdateVictim()) - return; - - if (SpellSummonVoidTimer <= diff) - { - DoCastVictim(SPELL_SUMMON_VOID_SENTRY, false); - SpellSummonVoidTimer = 20000; - } else SpellSummonVoidTimer -=diff; - - if (SpellVoidShiftTimer <= diff) - { - if (Unit* unit = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(unit, SPELL_VOID_SHIFT); - SpellVoidShiftTimer = 20000; - } else SpellVoidShiftTimer -=diff; - - if (SpellShroudOfDarknessTimer <= diff) - { - DoCastVictim(SPELL_SHROUD_OF_DARKNESS); - SpellShroudOfDarknessTimer = 20000; - } else SpellShroudOfDarknessTimer -=diff; - - DoMeleeAttackIfReady(); + sentries.Summon(summon); } void SummonedCreatureDies(Creature* summoned, Unit* /*who*/) override @@ -167,30 +146,73 @@ public: void JustDied(Unit* /*killer*/) override { + instance->SetData(DATA_ZURAMAT, 1); + Talk(SAY_DEATH); + DespawnSentries(); + if (instance->GetData(DATA_WAVE_COUNT) == 6) { - instance->SetData(DATA_1ST_BOSS_EVENT, DONE); + instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE); instance->SetData(DATA_WAVE_COUNT, 7); } else if (instance->GetData(DATA_WAVE_COUNT) == 12) { - instance->SetData(DATA_2ND_BOSS_EVENT, DONE); + instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE); instance->SetData(DATA_WAVE_COUNT, 13); } } void KilledUnit(Unit* victim) override { - if (victim->GetTypeId() != TYPEID_PLAYER) + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) return; - Talk(SAY_SLAY); + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (uint32 eventId = 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(); } + 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 diff --git a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp index ef9ad806c89..652b4815be0 100644 --- a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp @@ -22,8 +22,6 @@ #include "Player.h" #include "TemporarySummon.h" -#define MAX_ENCOUNTER 3 - /* Violet Hold encounters: 0 - First boss 1 - Second boss @@ -38,21 +36,6 @@ 6 - Zuramat 7 - Cyanigosa */ -enum GameObjects -{ - GO_MAIN_DOOR = 191723, - GO_XEVOZZ_DOOR = 191556, - GO_LAVANTHOR_DOOR = 191566, - GO_ICHORON_DOOR = 191722, - GO_ZURAMAT_DOOR = 191565, - GO_EREKEM_DOOR = 191564, - 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 -}; - enum AzureSaboteurSpells { SABOTEUR_SHIELD_DISRUPTION = 58291, @@ -64,7 +47,7 @@ enum CrystalSpells SPELL_ARCANE_LIGHTNING = 57930 }; -const Position PortalLocation[] = +Position const PortalLocation[] = { {1877.51f, 850.104f, 44.6599f, 4.7822f }, // WP 1 {1918.37f, 853.437f, 47.1624f, 4.12294f}, // WP 2 @@ -74,21 +57,21 @@ const Position PortalLocation[] = {1908.31f, 809.657f, 38.7037f, 3.08701f} // WP 6 }; -const Position ArcaneSphere = {1887.060059f, 806.151001f, 61.321602f, 0.0f}; -const Position BossStartMove1 = {1894.684448f, 739.390503f, 47.668003f, 0.0f}; -const Position BossStartMove2 = {1875.173950f, 860.832703f, 43.333565f, 0.0f}; -const Position BossStartMove21 = {1858.854614f, 855.071411f, 43.333565f, 0.0f}; -const Position BossStartMove22 = {1891.926636f, 863.388977f, 43.333565f, 0.0f}; -const Position BossStartMove3 = {1916.138062f, 778.152222f, 35.772308f, 0.0f}; -const Position BossStartMove4 = {1853.618286f, 758.557617f, 38.657505f, 0.0f}; -const Position BossStartMove5 = {1906.683960f, 842.348022f, 38.637459f, 0.0f}; -const Position BossStartMove6 = {1928.207031f, 852.864441f, 47.200813f, 0.0f}; - -const Position CyanigosasSpawnLocation = {1930.281250f, 804.407715f, 52.410946f, 3.139621f}; -const Position MiddleRoomLocation = {1892.291260f, 805.696838f, 38.438862f, 3.139621f}; -const Position MiddleRoomPortalSaboLocation = {1896.622925f, 804.854126f, 38.504772f, 3.139621f}; - -//Cyanigosa's prefight event data +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 @@ -100,21 +83,45 @@ enum Spells CYANIGOSA_BLUE_AURA = 47759, }; +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 +}; + +ObjectData const gameObjectData[] = +{ + { GO_EREKEM_GUARD_1_DOOR, DATA_EREKEM_LEFT_GUARD_CELL }, + { GO_EREKEM_GUARD_2_DOOR, DATA_EREKEM_RIGHT_GUARD_CELL }, + { GO_EREKEM_DOOR, DATA_EREKEM_CELL }, + { GO_ZURAMAT_DOOR, DATA_ZURAMAT_CELL }, + { GO_LAVANTHOR_DOOR, DATA_LAVANTHOR_CELL }, + { GO_MORAGG_DOOR, DATA_MORAGG_CELL }, + { GO_ICHORON_DOOR, DATA_ICHORON_CELL }, + { GO_XEVOZZ_DOOR, DATA_XEVOZZ_CELL }, + { GO_MAIN_DOOR, DATA_MAIN_DOOR }, + { 0, 0 } // END +}; + class instance_violet_hold : public InstanceMapScript { public: instance_violet_hold() : InstanceMapScript("instance_violet_hold", 608) { } - InstanceScript* GetInstanceScript(InstanceMap* map) const override - { - return new instance_violet_hold_InstanceMapScript(map); - } - struct instance_violet_hold_InstanceMapScript : public InstanceScript { instance_violet_hold_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); + SetBossNumber(EncounterCount); + LoadObjectData(creatureData, gameObjectData); uiRemoveNpc = 0; @@ -138,29 +145,11 @@ public: bCrystalActivated = false; defenseless = true; uiMainEventPhase = NOT_STARTED; - - memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + zuramatDead = false; } - ObjectGuid uiMoragg; - ObjectGuid uiErekem; ObjectGuid uiErekemGuard[2]; - ObjectGuid uiIchoron; - ObjectGuid uiLavanthor; - ObjectGuid uiXevozz; - ObjectGuid uiZuramat; - ObjectGuid uiCyanigosa; - ObjectGuid uiSinclari; - - ObjectGuid uiMoraggCell; - ObjectGuid uiErekemCell; - ObjectGuid uiErekemLeftGuardCell; - ObjectGuid uiErekemRightGuardCell; - ObjectGuid uiIchoronCell; - ObjectGuid uiLavanthorCell; - ObjectGuid uiXevozzCell; - ObjectGuid uiZuramatCell; - ObjectGuid uiMainDoor; + ObjectGuid uiTeleportationPortal; ObjectGuid uiSaboteurPortal; @@ -180,7 +169,6 @@ public: uint8 uiDoorIntegrity; - uint16 m_auiEncounter[MAX_ENCOUNTER]; uint8 uiCountErekemGuards; uint8 uiCountActivationCrystals; uint8 uiCyanigosaEventPhase; @@ -191,132 +179,93 @@ public: bool bIsDoorSpellCast; bool bCrystalActivated; bool defenseless; + bool zuramatDead; std::list<uint8> NpcAtDoorCastingList; - std::string str_data; - - bool IsEncounterInProgress() const override - { - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (m_auiEncounter[i] == IN_PROGRESS) - return true; - - return false; - } - void OnCreatureCreate(Creature* creature) override { + InstanceScript::OnCreatureCreate(creature); + switch (creature->GetEntry()) { - case CREATURE_XEVOZZ: - uiXevozz = creature->GetGUID(); - break; - case CREATURE_LAVANTHOR: - uiLavanthor = creature->GetGUID(); - break; - case CREATURE_ICHORON: - uiIchoron = creature->GetGUID(); - break; - case CREATURE_ZURAMAT: - uiZuramat = creature->GetGUID(); - break; - case CREATURE_EREKEM: - uiErekem = creature->GetGUID(); - break; - case CREATURE_EREKEM_GUARD: + 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); + creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); } break; - case CREATURE_MORAGG: - uiMoragg = creature->GetGUID(); + case NPC_CYANIGOSA: + creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); break; - case CREATURE_CYANIGOSA: - uiCyanigosa = creature->GetGUID(); - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); + default: break; - case CREATURE_SINCLARI: - uiSinclari = creature->GetGUID(); + case NPC_VOID_SENTRY: + if (zuramatDead) + { + creature->DespawnOrUnsummon(); + zuramatDead = false; + } break; } - /* - BEWARE - SHIT. if (creature->GetGUID() == uiFirstBoss || creature->GetGUID() == uiSecondBoss) { creature->AllLootRemovedFromCorpse(); creature->RemoveLootMode(1); } - */ } void OnGameObjectCreate(GameObject* go) override { + InstanceScript::OnGameObjectCreate(go); + switch (go->GetEntry()) { - case GO_EREKEM_GUARD_1_DOOR: - uiErekemLeftGuardCell = go->GetGUID(); - break; - case GO_EREKEM_GUARD_2_DOOR: - uiErekemRightGuardCell = go->GetGUID(); - break; - case GO_EREKEM_DOOR: - uiErekemCell = go->GetGUID(); - break; - case GO_ZURAMAT_DOOR: - uiZuramatCell = go->GetGUID(); - break; - case GO_LAVANTHOR_DOOR: - uiLavanthorCell = go->GetGUID(); - break; - case GO_MORAGG_DOOR: - uiMoraggCell = go->GetGUID(); - break; - case GO_ICHORON_DOOR: - uiIchoronCell = go->GetGUID(); - break; - case GO_XEVOZZ_DOOR: - uiXevozzCell = go->GetGUID(); - break; - case GO_MAIN_DOOR: - uiMainDoor = go->GetGUID(); - break; case GO_ACTIVATION_CRYSTAL: if (uiCountActivationCrystals < 4) uiActivationCrystal[uiCountActivationCrystals++] = go->GetGUID(); break; + default: + break; } } - void SetData(uint32 type, uint32 data) override + bool SetBossState(uint32 type, EncounterState state) override { + if (!InstanceScript::SetBossState(type, state)) + return false; + switch (type) { case DATA_1ST_BOSS_EVENT: - UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, CREATURE_EREKEM, NULL); - m_auiEncounter[0] = data; - if (data == DONE) - SaveToDB(); + if (state == DONE) + UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_EREKEM, nullptr); break; case DATA_2ND_BOSS_EVENT: - UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, CREATURE_MORAGG, NULL); - m_auiEncounter[1] = data; - if (data == DONE) - SaveToDB(); - break; - case DATA_CYANIGOSA_EVENT: - m_auiEncounter[2] = data; - if (data == DONE) + if (state == DONE) + UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_MORAGG, nullptr); + break; + case DATA_CYANIGOSA: + if (state == DONE) { - SaveToDB(); uiMainEventPhase = DONE; - if (GameObject* pMainDoor = instance->GetGameObject(uiMainDoor)) - pMainDoor->SetGoState(GO_STATE_ACTIVE); + if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR)) + mainDoor->SetGoState(GO_STATE_ACTIVE); } break; + default: + break; + } + + return true; + } + + void SetData(uint32 type, uint32 data) override + { + switch (type) + { case DATA_WAVE_COUNT: uiWaveCount = data; bActive = true; @@ -340,21 +289,8 @@ public: NpcAtDoorCastingList.pop_back(); break; case DATA_MAIN_DOOR: - if (GameObject* pMainDoor = instance->GetGameObject(uiMainDoor)) - { - switch (data) - { - case GO_STATE_ACTIVE: - pMainDoor->SetGoState(GO_STATE_ACTIVE); - break; - case GO_STATE_READY: - pMainDoor->SetGoState(GO_STATE_READY); - break; - case GO_STATE_ACTIVE_ALTERNATIVE: - pMainDoor->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); - break; - } - } + if (GameObject* mainDoor = GetGameObject(type)) + mainDoor->SetGoState(GOState(data)); break; case DATA_START_BOSS_ENCOUNTER: switch (uiWaveCount) @@ -374,7 +310,7 @@ public: uiMainEventPhase = data; if (data == IN_PROGRESS) // Start event { - if (GameObject* mainDoor = instance->GetGameObject(uiMainDoor)) + if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR)) mainDoor->SetGoState(GO_STATE_READY); uiWaveCount = 1; bActive = true; @@ -384,6 +320,9 @@ public: uiRemoveNpc = 0; // might not have been reset after a wipe on a boss. } break; + case DATA_ZURAMAT: + zuramatDead = true; + break; } } @@ -404,9 +343,6 @@ public: { switch (type) { - case DATA_1ST_BOSS_EVENT: return m_auiEncounter[0]; - case DATA_2ND_BOSS_EVENT: return m_auiEncounter[1]; - case DATA_CYANIGOSA_EVENT: return m_auiEncounter[2]; case DATA_WAVE_COUNT: return uiWaveCount; case DATA_REMOVE_NPC: return uiRemoveNpc; case DATA_PORTAL_LOCATION: return uiLocation; @@ -421,125 +357,114 @@ public: return 0; } - ObjectGuid GetGuidData(uint32 identifier) const override + ObjectGuid GetGuidData(uint32 type) const override { - switch (identifier) + switch (type) { - case DATA_MORAGG: return uiMoragg; - case DATA_EREKEM: return uiErekem; case DATA_EREKEM_GUARD_1: return uiErekemGuard[0]; case DATA_EREKEM_GUARD_2: return uiErekemGuard[1]; - case DATA_ICHORON: return uiIchoron; - case DATA_LAVANTHOR: return uiLavanthor; - case DATA_XEVOZZ: return uiXevozz; - case DATA_ZURAMAT: return uiZuramat; - case DATA_CYANIGOSA: return uiCyanigosa; - case DATA_MORAGG_CELL: return uiMoraggCell; - case DATA_EREKEM_CELL: return uiErekemCell; - case DATA_EREKEM_RIGHT_GUARD_CELL: return uiErekemRightGuardCell; - case DATA_EREKEM_LEFT_GUARD_CELL: return uiErekemLeftGuardCell; - case DATA_ICHORON_CELL: return uiIchoronCell; - case DATA_LAVANTHOR_CELL: return uiLavanthorCell; - case DATA_XEVOZZ_CELL: return uiXevozzCell; - case DATA_ZURAMAT_CELL: return uiZuramatCell; - case DATA_MAIN_DOOR: return uiMainDoor; - case DATA_SINCLARI: return uiSinclari; case DATA_TELEPORTATION_PORTAL: return uiTeleportationPortal; case DATA_SABOTEUR_PORTAL: return uiSaboteurPortal; } - return ObjectGuid::Empty; + return InstanceScript::GetGuidData(type); } void SpawnPortal() { SetData(DATA_PORTAL_LOCATION, (GetData(DATA_PORTAL_LOCATION) + urand(1, 5))%6); - if (Creature* pSinclari = instance->GetCreature(uiSinclari)) - if (Creature* portal = pSinclari->SummonCreature(CREATURE_TELEPORTATION_PORTAL, PortalLocation[GetData(DATA_PORTAL_LOCATION)], TEMPSUMMON_CORPSE_DESPAWN)) + 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(); } void StartBossEncounter(uint8 uiBoss, bool bForceRespawn = true) { - Creature* pBoss = NULL; + Creature* boss = nullptr; switch (uiBoss) { case BOSS_MORAGG: - HandleGameObject(uiMoraggCell, bForceRespawn); - pBoss = instance->GetCreature(uiMoragg); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove1); + HandleGameObject(GetObjectGuid(DATA_MORAGG_CELL), bForceRespawn); + boss = GetCreature(DATA_MORAGG); + if (boss) + boss->GetMotionMaster()->MovePoint(0, BossStartMove1); break; case BOSS_EREKEM: - HandleGameObject(uiErekemCell, bForceRespawn); - HandleGameObject(uiErekemRightGuardCell, bForceRespawn); - HandleGameObject(uiErekemLeftGuardCell, bForceRespawn); - - pBoss = instance->GetCreature(uiErekem); + HandleGameObject(GetObjectGuid(DATA_EREKEM_CELL), bForceRespawn); + HandleGameObject(GetObjectGuid(DATA_EREKEM_LEFT_GUARD_CELL), bForceRespawn); + HandleGameObject(GetObjectGuid(DATA_EREKEM_RIGHT_GUARD_CELL), bForceRespawn); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove2); + boss = GetCreature(DATA_EREKEM); + if (boss) + boss->GetMotionMaster()->MovePoint(0, BossStartMove2); if (Creature* pGuard1 = instance->GetCreature(uiErekemGuard[0])) { if (bForceRespawn) - pGuard1->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); + { + pGuard1->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE); + pGuard1->GetMotionMaster()->MovePoint(0, BossStartMove21); + } else pGuard1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - pGuard1->GetMotionMaster()->MovePoint(0, BossStartMove21); } if (Creature* pGuard2 = instance->GetCreature(uiErekemGuard[1])) { if (bForceRespawn) + { pGuard2->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); + pGuard2->GetMotionMaster()->MovePoint(0, BossStartMove22); + } else pGuard2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - pGuard2->GetMotionMaster()->MovePoint(0, BossStartMove22); } break; case BOSS_ICHORON: - HandleGameObject(uiIchoronCell, bForceRespawn); - pBoss = instance->GetCreature(uiIchoron); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove3); + HandleGameObject(GetObjectGuid(DATA_ICHORON_CELL), bForceRespawn); + boss = GetCreature(DATA_ICHORON); + if (boss) + boss->GetMotionMaster()->MovePoint(0, BossStartMove3); break; case BOSS_LAVANTHOR: - HandleGameObject(uiLavanthorCell, bForceRespawn); - pBoss = instance->GetCreature(uiLavanthor); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove4); + HandleGameObject(GetObjectGuid(DATA_LAVANTHOR_CELL), bForceRespawn); + boss = GetCreature(DATA_LAVANTHOR); + if (boss) + boss->GetMotionMaster()->MovePoint(0, BossStartMove4); break; case BOSS_XEVOZZ: - HandleGameObject(uiXevozzCell, bForceRespawn); - pBoss = instance->GetCreature(uiXevozz); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove5); + HandleGameObject(GetObjectGuid(DATA_XEVOZZ_CELL), bForceRespawn); + boss = GetCreature(DATA_XEVOZZ); + if (boss) + boss->GetMotionMaster()->MovePoint(0, BossStartMove5); break; case BOSS_ZURAMAT: - HandleGameObject(uiZuramatCell, bForceRespawn); - pBoss = instance->GetCreature(uiZuramat); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove6); + HandleGameObject(GetObjectGuid(DATA_ZURAMAT_CELL), bForceRespawn); + boss = GetCreature(DATA_ZURAMAT); + if (boss) + boss->GetMotionMaster()->MovePoint(0, BossStartMove6); break; } // generic boss state changes - if (pBoss) + if (boss) { - pBoss->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - pBoss->SetReactState(REACT_AGGRESSIVE); + boss->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); + boss->SetReactState(REACT_AGGRESSIVE); if (!bForceRespawn) { - if (pBoss->isDead()) + if (boss->isDead()) { // respawn but avoid to be looted again - pBoss->Respawn(); - pBoss->RemoveLootMode(1); + boss->Respawn(); + boss->RemoveLootMode(1); } - pBoss->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); + else + boss->GetMotionMaster()->MoveTargetedHome(); + + boss->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); uiWaveCount = 0; } } @@ -555,12 +480,12 @@ public: case 6: if (uiFirstBoss == 0) uiFirstBoss = urand(1, 6); - if (Creature* pSinclari = instance->GetCreature(uiSinclari)) + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) { - if (Creature* pPortal = pSinclari->SummonCreature(CREATURE_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN)) - uiSaboteurPortal = pPortal->GetGUID(); - if (Creature* pAzureSaboteur = pSinclari->SummonCreature(CREATURE_SABOTEOUR, MiddleRoomLocation, TEMPSUMMON_DEAD_DESPAWN)) - pAzureSaboteur->CastSpell(pAzureSaboteur, SABOTEUR_SHIELD_EFFECT, false); + 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: @@ -569,25 +494,22 @@ public: { uiSecondBoss = urand(1, 6); } while (uiSecondBoss == uiFirstBoss); - if (Creature* pSinclari = instance->GetCreature(uiSinclari)) + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) { - if (Creature* pPortal = pSinclari->SummonCreature(CREATURE_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN)) + if (Creature* pPortal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN)) uiSaboteurPortal = pPortal->GetGUID(); - if (Creature* pAzureSaboteur = pSinclari->SummonCreature(CREATURE_SABOTEOUR, MiddleRoomLocation, TEMPSUMMON_DEAD_DESPAWN)) + if (Creature* pAzureSaboteur = sinclari->SummonCreature(NPC_SABOTEOUR, MiddleRoomLocation, TEMPSUMMON_DEAD_DESPAWN)) pAzureSaboteur->CastSpell(pAzureSaboteur, SABOTEUR_SHIELD_EFFECT, false); } break; case 18: - { - Creature* pSinclari = instance->GetCreature(uiSinclari); - if (pSinclari) - pSinclari->SummonCreature(CREATURE_CYANIGOSA, CyanigosasSpawnLocation, TEMPSUMMON_DEAD_DESPAWN); + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) + sinclari->SummonCreature(NPC_CYANIGOSA, CyanigosasSpawnLocation, TEMPSUMMON_DEAD_DESPAWN); break; - } case 1: { - if (GameObject* pMainDoor = instance->GetGameObject(uiMainDoor)) - pMainDoor->SetGoState(GO_STATE_READY); + if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR)) + mainDoor->SetGoState(GO_STATE_READY); DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, 100); // no break } @@ -597,54 +519,15 @@ public: } } - std::string GetSaveData() override + void WriteSaveDataMore(std::ostringstream& data) override { - OUT_SAVE_INST_DATA; - - std::ostringstream saveStream; - saveStream << "V H " << (uint16)m_auiEncounter[0] - << ' ' << (uint16)m_auiEncounter[1] - << ' ' << (uint16)m_auiEncounter[2] - << ' ' << (uint16)uiFirstBoss - << ' ' << (uint16)uiSecondBoss; - - str_data = saveStream.str(); - - OUT_SAVE_INST_DATA_COMPLETE; - return str_data; + data << uiFirstBoss << ' ' << uiSecondBoss; } - void Load(const char* in) override + void ReadSaveDataMore(std::istringstream& data) override { - if (!in) - { - OUT_LOAD_INST_DATA_FAIL; - return; - } - - OUT_LOAD_INST_DATA(in); - - char dataHead1, dataHead2; - uint16 data0, data1, data2, data3, data4; - - std::istringstream loadStream(in); - loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3 >> data4; - - if (dataHead1 == 'V' && dataHead2 == 'H') - { - m_auiEncounter[0] = data0; - m_auiEncounter[1] = data1; - m_auiEncounter[2] = data2; - - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (m_auiEncounter[i] == IN_PROGRESS) - m_auiEncounter[i] = NOT_STARTED; - - uiFirstBoss = uint8(data3); - uiSecondBoss = uint8(data4); - } else OUT_LOAD_INST_DATA_FAIL; - - OUT_LOAD_INST_DATA_COMPLETE; + data >> uiFirstBoss; + data >> uiSecondBoss; } bool CheckWipe() @@ -660,6 +543,7 @@ public: return false; } + zuramatDead = false; return true; } @@ -690,59 +574,57 @@ public: 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* pSinclari = instance->GetCreature(uiSinclari)) + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) { - pSinclari->SetVisible(true); + sinclari->SetVisible(true); std::list<Creature*> GuardList; - pSinclari->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); + sinclari->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); if (!GuardList.empty()) { - for (std::list<Creature*>::const_iterator itr = GuardList.begin(); itr != GuardList.end(); ++itr) + for (Creature* guard : GuardList) { - if (Creature* pGuard = *itr) - { - pGuard->SetVisible(true); - pGuard->SetReactState(REACT_AGGRESSIVE); - pGuard->GetMotionMaster()->MovePoint(1, pGuard->GetHomePosition()); - } + guard->SetVisible(true); + guard->SetReactState(REACT_AGGRESSIVE); + guard->GetMotionMaster()->MovePoint(1, guard->GetHomePosition()); } } - pSinclari->GetMotionMaster()->MovePoint(1, pSinclari->GetHomePosition()); - pSinclari->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + sinclari->GetMotionMaster()->MovePoint(1, sinclari->GetHomePosition()); + sinclari->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } } // Cyanigosa is spawned but not tranformed, prefight event - Creature* pCyanigosa = instance->GetCreature(uiCyanigosa); - if (pCyanigosa && !pCyanigosa->HasAura(CYANIGOSA_SPELL_TRANSFORM)) + Creature* cyanigosa = GetCreature(DATA_CYANIGOSA); + if (cyanigosa && !cyanigosa->HasAura(CYANIGOSA_SPELL_TRANSFORM)) { if (uiCyanigosaEventTimer <= diff) { switch (uiCyanigosaEventPhase) { case 1: - pCyanigosa->CastSpell(pCyanigosa, CYANIGOSA_BLUE_AURA, false); - pCyanigosa->AI()->Talk(CYANIGOSA_SAY_SPAWN); + cyanigosa->CastSpell(cyanigosa, CYANIGOSA_BLUE_AURA, false); + cyanigosa->AI()->Talk(CYANIGOSA_SAY_SPAWN); uiCyanigosaEventTimer = 7*IN_MILLISECONDS; ++uiCyanigosaEventPhase; break; case 2: - pCyanigosa->GetMotionMaster()->MoveJump(MiddleRoomLocation.GetPositionX(), MiddleRoomLocation.GetPositionY(), MiddleRoomLocation.GetPositionZ(), 10.0f, 20.0f); - pCyanigosa->CastSpell(pCyanigosa, CYANIGOSA_BLUE_AURA, false); + 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: - pCyanigosa->RemoveAurasDueToSpell(CYANIGOSA_BLUE_AURA); - pCyanigosa->CastSpell(pCyanigosa, CYANIGOSA_SPELL_TRANSFORM, 0); - pCyanigosa->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE); - pCyanigosa->SetReactState(REACT_AGGRESSIVE); + 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; @@ -815,6 +697,11 @@ public: } } }; + + 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 645a9da4764..b05da4b994c 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp @@ -28,22 +28,23 @@ #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 enum PortalCreatures { - CREATURE_AZURE_INVADER_1 = 30661, - CREATURE_AZURE_INVADER_2 = 30961, - CREATURE_AZURE_SPELLBREAKER_1 = 30662, - CREATURE_AZURE_SPELLBREAKER_2 = 30962, - CREATURE_AZURE_BINDER_1 = 30663, - CREATURE_AZURE_BINDER_2 = 30918, - CREATURE_AZURE_MAGE_SLAYER_1 = 30664, - CREATURE_AZURE_MAGE_SLAYER_2 = 30963, - CREATURE_AZURE_CAPTAIN = 30666, - CREATURE_AZURE_SORCEROR = 30667, - CREATURE_AZURE_RAIDER = 30668, - CREATURE_AZURE_STALKER = 32191 + 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_AZURE_MAGE_SLAYER_2 = 30963, + NPC_AZURE_CAPTAIN = 30666, + NPC_AZURE_SORCEROR = 30667, + NPC_AZURE_RAIDER = 30668, + NPC_AZURE_STALKER = 32191 }; enum AzureInvaderSpells @@ -59,7 +60,7 @@ enum AzureSellbreakerSpells SPELL_ARCANE_BLAST = 58462, SPELL_SLOW = 25603, SPELL_CHAINS_OF_ICE = 58464, - SPELL_CONE_OF_COLD = 58463 + SPELL_CONE_OF_COLD = 58463, }; enum AzureBinderSpells @@ -67,7 +68,7 @@ enum AzureBinderSpells SPELL_ARCANE_BARRAGE = 58456, SPELL_ARCANE_EXPLOSION = 58455, SPELL_FROST_NOVA = 58458, - SPELL_FROSTBOLT = 58457 + SPELL_FROSTBOLT = 58457, }; enum AzureMageSlayerSpells @@ -85,7 +86,7 @@ enum AzureCaptainSpells enum AzureSorcerorSpells { SPELL_ARCANE_STREAM = 60181, - SPELL_MANA_DETONATION = 60182 + SPELL_MANA_DETONATION = 60182, }; enum AzureRaiderSpells @@ -114,7 +115,7 @@ enum TrashDoorSpell enum Spells { SPELL_PORTAL_CHANNEL = 58012, - SPELL_CRYSTAL_ACTIVATION = 57804, + SPELL_CRYSTAL_ACTIVATION = 57804, // visual effect SPELL_ARCANE_SPHERE_PASSIVE = 44263 }; @@ -242,9 +243,21 @@ const float SaboteurFinalPos6[5][3] = {1931.063354f, 848.468445f, 47.190434f} }; -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 = {1817.315674f, 804.060608f, 44.363998f, 0.0f}; +const Position PortalLocation[] = +{ + { 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 +}; + +#define MAX_PRE_EVENT_PORTAL 3 + +ObjectGuid preEventPortalGUID[MAX_PRE_EVENT_PORTAL] = { ObjectGuid::Empty }; + +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 }; class npc_sinclari_vh : public CreatureScript { @@ -258,7 +271,7 @@ public: { case GOSSIP_ACTION_INFO_DEF+1: player->CLOSE_GOSSIP_MENU(); - ENSURE_AI(npc_sinclari_vh::npc_sinclariAI, (creature->AI()))->uiPhase = 1; + ENSURE_AI(npc_sinclari_vh::npc_sinclariAI, creature->AI())->uiPhase = 1; if (InstanceScript* instance = creature->GetInstanceScript()) instance->SetData(DATA_MAIN_EVENT_PHASE, SPECIAL); break; @@ -296,17 +309,12 @@ public: return true; } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_sinclariAI>(creature); - } - struct npc_sinclariAI : public ScriptedAI { npc_sinclariAI(Creature* creature) : ScriptedAI(creature) { Initialize(); - instance = creature->GetInstanceScript(); + instance = creature->GetInstanceScript(); } void Initialize() @@ -325,6 +333,9 @@ public: Initialize(); 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(); std::list<Creature*> GuardList; me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f); @@ -345,8 +356,6 @@ public: void UpdateAI(uint32 uiDiff) override { - ScriptedAI::UpdateAI(uiDiff); - if (uiPhase) { if (uiTimer <= uiDiff) @@ -354,25 +363,17 @@ public: switch (uiPhase) { case 1: - Talk(SAY_SINCLARI_1); - uiTimer = 4000; - uiPhase = 2; + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(0, sinclariCrystalPosition); + uiTimer = 1000; + uiPhase = 6; break; case 2: { - 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->SetWalk(false); - pGuard->GetMotionMaster()->MovePoint(0, MovePosition); - } - } - uiTimer = 6000; - uiPhase = 3; + me->SetFacingTo(me->GetOrientation() - 3.14f); + Talk(SAY_SINCLARI_1); + uiTimer = 1500; + uiPhase = 7; break; } case 3: @@ -385,7 +386,6 @@ public: if (Creature* pGuard = *itr) { pGuard->SetVisible(false); - pGuard->SetReactState(REACT_PASSIVE); } } uiTimer = 2000; @@ -398,11 +398,58 @@ public: uiPhase = 5; break; case 5: - instance->SetData(DATA_MAIN_EVENT_PHASE, IN_PROGRESS); + 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; + break; + case 6: + me->GetMotionMaster()->MovementExpired(); + me->HandleEmoteCommand(EMOTE_STATE_USE_STANDING); + uiTimer = 2000; + uiPhase = 2; + 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()) + { + for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr) + (*itr)->DisappearAndDie(); + } + uiTimer = 500; + uiPhase = 9; + } + break; + case 8: + instance->SetData(DATA_MAIN_EVENT_PHASE, IN_PROGRESS); uiTimer = 0; uiPhase = 0; 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; } } else uiTimer -= uiDiff; @@ -415,6 +462,10 @@ public: } }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_sinclariAI>(creature); + } }; class npc_azure_saboteur : public CreatureScript @@ -422,14 +473,9 @@ class npc_azure_saboteur : public CreatureScript public: npc_azure_saboteur() : CreatureScript("npc_azure_saboteur") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_saboteurAI>(creature); - } - struct npc_azure_saboteurAI : public npc_escortAI { - npc_azure_saboteurAI(Creature* creature):npc_escortAI(creature) + npc_azure_saboteurAI(Creature* creature) : npc_escortAI(creature) { instance = creature->GetInstanceScript(); bHasGotMovingPoints = false; @@ -532,13 +578,16 @@ public: { me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false); me->DisappearAndDie(); - Creature* pSaboPort = ObjectAccessor::GetCreature((*me), instance->GetGuidData(DATA_SABOTEUR_PORTAL)); - if (pSaboPort) + if (Creature* pSaboPort = ObjectAccessor::GetCreature((*me), instance->GetGuidData(DATA_SABOTEUR_PORTAL))) pSaboPort->DisappearAndDie(); instance->SetData(DATA_START_BOSS_ENCOUNTER, 1); } }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_azure_saboteurAI>(creature); + } }; class npc_teleportation_portal_vh : public CreatureScript @@ -546,11 +595,6 @@ class npc_teleportation_portal_vh : public CreatureScript public: npc_teleportation_portal_vh() : CreatureScript("npc_teleportation_portal_vh") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_teleportation_portalAI>(creature); - } - struct npc_teleportation_portalAI : public ScriptedAI { npc_teleportation_portalAI(Creature* creature) : ScriptedAI(creature), listOfMobs(me) @@ -558,6 +602,9 @@ public: Initialize(); instance = creature->GetInstanceScript(); uiTypeOfMobsPortal = urand(0, 1); // 0 - elite mobs 1 - portal guardian or portal keeper with regular mobs + + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == NOT_STARTED) + uiTypeOfMobsPortal = 2; } void Initialize() @@ -583,13 +630,15 @@ public: void MoveInLineOfSight(Unit* /*who*/) override { } - void UpdateAI(uint32 diff) override { - if (instance->GetData(DATA_REMOVE_NPC) == 1) + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) { - me->DespawnOrUnsummon(); - instance->SetData(DATA_REMOVE_NPC, 0); + if (instance->GetData(DATA_REMOVE_NPC) == 1) + { + me->DespawnOrUnsummon(); + instance->SetData(DATA_REMOVE_NPC, 0); + } } uint8 uiWaveCount = instance->GetData(DATA_WAVE_COUNT); @@ -608,7 +657,7 @@ public: uint8 k = uiWaveCount < 12 ? 2 : 3; for (uint8 i = 0; i < k; ++i) { - uint32 entry = RAND(CREATURE_AZURE_CAPTAIN, CREATURE_AZURE_RAIDER, CREATURE_AZURE_STALKER, CREATURE_AZURE_SORCEROR); + 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); @@ -633,14 +682,14 @@ public: uint8 k = instance->GetData(DATA_WAVE_COUNT) < 12 ? 3 : 4; for (uint8 i = 0; i < k; ++i) { - uint32 entry = RAND(CREATURE_AZURE_INVADER_1, CREATURE_AZURE_INVADER_2, CREATURE_AZURE_SPELLBREAKER_1, CREATURE_AZURE_SPELLBREAKER_2, CREATURE_AZURE_MAGE_SLAYER_1, CREATURE_AZURE_MAGE_SLAYER_2, CREATURE_AZURE_BINDER_1, CREATURE_AZURE_BINDER_2); + 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(CREATURE_PORTAL_GUARDIAN, CREATURE_PORTAL_KEEPER); + 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); } @@ -653,37 +702,71 @@ public: me->RemoveCorpse(); } break; + case 2: // Pre-event + if (uiSpawnTimer <= diff) + { + 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; } } void JustDied(Unit* /*killer*/) override { - instance->SetData(DATA_WAVE_COUNT, instance->GetData(DATA_WAVE_COUNT)+1); + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) + instance->SetData(DATA_WAVE_COUNT, instance->GetData(DATA_WAVE_COUNT) + 1); } void JustSummoned(Creature* summoned) override { - listOfMobs.Summon(summoned); - instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) + { + listOfMobs.Summon(summoned); + instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); + } } void SummonedCreatureDies(Creature* summoned, Unit* /*killer*/) override { - listOfMobs.Despawn(summoned); - instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID()); + if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS) + { + listOfMobs.Despawn(summoned); + instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID()); + } } }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_teleportation_portalAI>(creature); + } }; struct violet_hold_trashAI : public npc_escortAI { - violet_hold_trashAI(Creature* creature):npc_escortAI(creature) + violet_hold_trashAI(Creature* creature) : npc_escortAI(creature) { instance = creature->GetInstanceScript(); bHasGotMovingPoints = false; - portalLocationID = instance->GetData(DATA_PORTAL_LOCATION); - secondPortalRouteID = 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 + { + portalLocationID = instance->GetData(DATA_PORTAL_LOCATION); + Reset(); + } } public: @@ -698,7 +781,7 @@ struct violet_hold_trashAI : public npc_escortAI { case 0: if (waypointId == 5) - CreatureStartAttackDoor(); + CreatureStartAttackDoor(); break; case 1: if ((waypointId == 8 && secondPortalRouteID == 0) || (waypointId == 7 && secondPortalRouteID == 1)) @@ -706,7 +789,7 @@ struct violet_hold_trashAI : public npc_escortAI break; case 2: if (waypointId == 7) - CreatureStartAttackDoor(); + CreatureStartAttackDoor(); break; case 3: if (waypointId == 8) @@ -723,7 +806,7 @@ struct violet_hold_trashAI : public npc_escortAI } } - void UpdateAI(uint32) override + void UpdateAI(uint32 diff) override { if (instance->GetData(DATA_MAIN_EVENT_PHASE) != IN_PROGRESS) me->CastStop(); @@ -778,6 +861,8 @@ struct violet_hold_trashAI : public npc_escortAI SetDespawnAtEnd(false); Start(true, true); } + + npc_escortAI::UpdateAI(diff); } void JustDied(Unit* /*killer*/) override @@ -791,7 +876,6 @@ struct violet_hold_trashAI : public npc_escortAI DoCast(SPELL_DESTROY_DOOR_SEAL); instance->SetData(DATA_NPC_PRESENCE_AT_DOOR_ADD, 1); } - }; class npc_azure_invader : public CreatureScript @@ -799,11 +883,6 @@ class npc_azure_invader : public CreatureScript public: npc_azure_invader() : CreatureScript("npc_azure_invader") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_invaderAI>(creature); - } - struct npc_azure_invaderAI : public violet_hold_trashAI { npc_azure_invaderAI(Creature* creature) : violet_hold_trashAI(creature) @@ -833,12 +912,11 @@ public: void UpdateAI(uint32 diff) override { violet_hold_trashAI::UpdateAI(diff); - npc_escortAI::UpdateAI(diff); if (!UpdateVictim()) return; - if (me->GetEntry() == CREATURE_AZURE_INVADER_1) + if (me->GetEntry() == NPC_AZURE_INVADER_1) { if (uiCleaveTimer <= diff) { @@ -848,14 +926,13 @@ public: if (uiImpaleTimer <= diff) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) DoCast(target, SPELL_IMPALE); uiImpaleTimer = 4000; } else uiImpaleTimer -= diff; } - if (me->GetEntry() == CREATURE_AZURE_INVADER_2) + if (me->GetEntry() == NPC_AZURE_INVADER_2) { if (uiBrutalStrikeTimer <= diff) { @@ -876,6 +953,10 @@ public: } }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_azure_invaderAI>(creature); + } }; class npc_azure_binder : public CreatureScript @@ -883,11 +964,6 @@ class npc_azure_binder : public CreatureScript public: npc_azure_binder() : CreatureScript("npc_azure_binder") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_binderAI>(creature); - } - struct npc_azure_binderAI : public violet_hold_trashAI { npc_azure_binderAI(Creature* creature) : violet_hold_trashAI(creature) @@ -917,12 +993,11 @@ public: void UpdateAI(uint32 diff) override { violet_hold_trashAI::UpdateAI(diff); - npc_escortAI::UpdateAI(diff); if (!UpdateVictim()) return; - if (me->GetEntry() == CREATURE_AZURE_BINDER_1) + if (me->GetEntry() == NPC_AZURE_BINDER_1) { if (uiArcaneExplosionTimer <= diff) { @@ -930,16 +1005,15 @@ public: uiArcaneExplosionTimer = 5000; } else uiArcaneExplosionTimer -= diff; - if (uiArcainBarrageTimer <= diff) + if (uiArcainBarrageTimer <= diff) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true); - if (target) - DoCast(target, SPELL_ARCANE_BARRAGE); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + DoCast(target, SPELL_ARCANE_BARRAGE); uiArcainBarrageTimer = 6000; } else uiArcainBarrageTimer -= diff; } - if (me->GetEntry() == CREATURE_AZURE_BINDER_2) + if (me->GetEntry() == NPC_AZURE_BINDER_2) { if (uiFrostNovaTimer <= diff) { @@ -949,8 +1023,7 @@ public: if (uiFrostboltTimer <= diff) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) DoCast(target, SPELL_FROSTBOLT); uiFrostboltTimer = 6000; } else uiFrostboltTimer -= diff; @@ -960,6 +1033,10 @@ public: } }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_azure_binderAI>(creature); + } }; class npc_azure_mage_slayer : public CreatureScript @@ -967,11 +1044,6 @@ class npc_azure_mage_slayer : public CreatureScript public: npc_azure_mage_slayer() : CreatureScript("npc_azure_mage_slayer") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_mage_slayerAI>(creature); - } - struct npc_azure_mage_slayerAI : public violet_hold_trashAI { npc_azure_mage_slayerAI(Creature* creature) : violet_hold_trashAI(creature) @@ -997,12 +1069,11 @@ public: void UpdateAI(uint32 diff) override { violet_hold_trashAI::UpdateAI(diff); - npc_escortAI::UpdateAI(diff); if (!UpdateVictim()) return; - if (me->GetEntry() == CREATURE_AZURE_MAGE_SLAYER_1) + if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_1) { if (uiArcaneEmpowermentTimer <= diff) { @@ -1011,12 +1082,11 @@ public: } else uiArcaneEmpowermentTimer -= diff; } - if (me->GetEntry() == CREATURE_AZURE_MAGE_SLAYER_2) + if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_2) { if (uiSpellLockTimer <= diff) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) DoCast(target, SPELL_SPELL_LOCK); uiSpellLockTimer = 9000; } else uiSpellLockTimer -= diff; @@ -1026,6 +1096,10 @@ public: } }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_azure_mage_slayerAI>(creature); + } }; class npc_azure_raider : public CreatureScript @@ -1033,11 +1107,6 @@ class npc_azure_raider : public CreatureScript public: npc_azure_raider() : CreatureScript("npc_azure_raider") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_raiderAI>(creature); - } - struct npc_azure_raiderAI : public violet_hold_trashAI { npc_azure_raiderAI(Creature* creature) : violet_hold_trashAI(creature) @@ -1063,7 +1132,6 @@ public: void UpdateAI(uint32 diff) override { violet_hold_trashAI::UpdateAI(diff); - npc_escortAI::UpdateAI(diff); if (!UpdateVictim()) return; @@ -1084,6 +1152,10 @@ public: } }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_azure_raiderAI>(creature); + } }; class npc_azure_stalker : public CreatureScript @@ -1114,7 +1186,6 @@ public: void UpdateAI(uint32 diff) override { violet_hold_trashAI::UpdateAI(diff); - npc_escortAI::UpdateAI(diff); if (!UpdateVictim()) return; @@ -1123,8 +1194,7 @@ public: { if (_tacticalBlinkTimer <= diff) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40, true); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40, true)) DoCast(target, SPELL_TACTICAL_BLINK); _tacticalBlinkTimer = 6000; _tacticalBlinkCast = true; @@ -1135,8 +1205,8 @@ public: { if (_backstabTimer <= diff) { - Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 10, true); - DoCast(target, SPELL_BACKSTAB); + if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 10, true)) + DoCast(target, SPELL_BACKSTAB); _tacticalBlinkCast = false; _backstabTimer =1300; } else _backstabTimer -= diff; @@ -1191,43 +1261,39 @@ public: void UpdateAI(uint32 diff) override { violet_hold_trashAI::UpdateAI(diff); - npc_escortAI::UpdateAI(diff); if (!UpdateVictim()) return; - if (me->GetEntry() == CREATURE_AZURE_SPELLBREAKER_1) + if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_1) { if (uiArcaneBlastTimer <= diff) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) DoCast(target, SPELL_ARCANE_BLAST); uiArcaneBlastTimer = 6000; } else uiArcaneBlastTimer -= diff; if (uiSlowTimer <= diff) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) DoCast(target, SPELL_SLOW); uiSlowTimer = 5000; } else uiSlowTimer -= diff; } - if (me->GetEntry() == CREATURE_AZURE_SPELLBREAKER_2) + if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_2) { if (uiChainsOfIceTimer <= diff) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) DoCast(target, SPELL_CHAINS_OF_ICE); uiChainsOfIceTimer = 7000; } else uiChainsOfIceTimer -= diff; if (uiConeOfColdTimer <= diff) { - DoCast(SPELL_CONE_OF_COLD); + DoCast(SPELL_CONE_OF_COLD); uiConeOfColdTimer = 5000; } else uiConeOfColdTimer -= diff; } @@ -1247,11 +1313,6 @@ class npc_azure_captain : public CreatureScript public: npc_azure_captain() : CreatureScript("npc_azure_captain") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_captainAI>(creature); - } - struct npc_azure_captainAI : public violet_hold_trashAI { npc_azure_captainAI(Creature* creature) : violet_hold_trashAI(creature) @@ -1277,7 +1338,6 @@ public: void UpdateAI(uint32 diff) override { violet_hold_trashAI::UpdateAI(diff); - npc_escortAI::UpdateAI(diff); if (!UpdateVictim()) return; @@ -1298,6 +1358,10 @@ public: } }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_azure_captainAI>(creature); + } }; class npc_azure_sorceror : public CreatureScript @@ -1305,11 +1369,6 @@ class npc_azure_sorceror : public CreatureScript public: npc_azure_sorceror() : CreatureScript("npc_azure_sorceror") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_azure_sorcerorAI>(creature); - } - struct npc_azure_sorcerorAI : public violet_hold_trashAI { npc_azure_sorcerorAI(Creature* creature) : violet_hold_trashAI(creature) @@ -1337,15 +1396,13 @@ public: void UpdateAI(uint32 diff) override { violet_hold_trashAI::UpdateAI(diff); - npc_escortAI::UpdateAI(diff); if (!UpdateVictim()) return; if (uiArcaneStreamTimer <= diff) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) DoCast(target, SPELL_ARCANE_STREAM); uiArcaneStreamTimer = urand(0, 5000)+5000; uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer; @@ -1360,8 +1417,12 @@ public: DoMeleeAttackIfReady(); } }; -}; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_azure_sorcerorAI>(creature); + } +}; class npc_violet_hold_arcane_sphere : public CreatureScript { @@ -1419,6 +1480,33 @@ public: } }; +class spell_crystal_activation : public SpellScriptLoader +{ +public: + spell_crystal_activation() : SpellScriptLoader("spell_crystal_activation") { } + + class spell_crystal_activation_SpellScript : public SpellScript + { + PrepareSpellScript(spell_crystal_activation_SpellScript); + + void HandleSendEvent(SpellEffIndex effIndex) + { + if (GetHitUnit()->GetEntry() == NPC_VIOLET_HOLD_GUARD) + PreventHitDefaultEffect(effIndex); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_crystal_activation_SpellScript::HandleSendEvent, EFFECT_0, SPELL_EFFECT_SEND_EVENT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_crystal_activation_SpellScript(); + } +}; + void AddSC_violet_hold() { new npc_sinclari_vh(); @@ -1434,4 +1522,5 @@ void AddSC_violet_hold() new npc_azure_saboteur(); new npc_violet_hold_arcane_sphere(); new go_activation_crystal(); + new spell_crystal_activation(); } diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.h b/src/server/scripts/Northrend/VioletHold/violet_hold.h index 404d1f493e6..e8da9576c13 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.h +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.h @@ -18,13 +18,18 @@ #ifndef DEF_VIOLET_HOLD_H #define DEF_VIOLET_HOLD_H -#define DataHeader "VIO" +#define DataHeader "VH" + +uint32 const EncounterCount = 3; enum Data { + // Main encounters DATA_1ST_BOSS_EVENT, DATA_2ND_BOSS_EVENT, - DATA_CYANIGOSA_EVENT, + DATA_CYANIGOSA, + + // Misc DATA_WAVE_COUNT, DATA_REMOVE_NPC, DATA_PORTAL_LOCATION, @@ -38,10 +43,8 @@ enum Data DATA_ACTIVATE_CRYSTAL, DATA_MAIN_EVENT_PHASE, DATA_DEFENSELESS, -}; -enum Data64 -{ + // Bosses DATA_MORAGG, DATA_EREKEM, DATA_EREKEM_GUARD_1, @@ -50,7 +53,8 @@ enum Data64 DATA_LAVANTHOR, DATA_XEVOZZ, DATA_ZURAMAT, - DATA_CYANIGOSA, + + // Cells DATA_MORAGG_CELL, DATA_EREKEM_CELL, DATA_EREKEM_LEFT_GUARD_CELL, @@ -59,6 +63,8 @@ enum Data64 DATA_LAVANTHOR_CELL, DATA_XEVOZZ_CELL, DATA_ZURAMAT_CELL, + + // Misc DATA_MAIN_DOOR, DATA_SINCLARI, DATA_TELEPORTATION_PORTAL, @@ -81,33 +87,50 @@ enum Bosses enum CreaturesIds { - CREATURE_TELEPORTATION_PORTAL = 31011, - CREATURE_PORTAL_GUARDIAN = 30660, - CREATURE_PORTAL_KEEPER = 30695, - CREATURE_XEVOZZ = 29266, - CREATURE_LAVANTHOR = 29312, - CREATURE_ICHORON = 29313, - CREATURE_ZURAMAT = 29314, - CREATURE_EREKEM = 29315, - CREATURE_EREKEM_GUARD = 29395, - CREATURE_MORAGG = 29316, - CREATURE_CYANIGOSA = 31134, - CREATURE_SINCLARI = 30658, - CREATURE_SABOTEOUR = 31079, - NPC_VIOLET_HOLD_GUARD = 30659, - NPC_DEFENSE_SYSTEM = 30837 + NPC_TELEPORTATION_PORTAL = 31011, + NPC_PORTAL_GUARDIAN = 30660, + NPC_PORTAL_KEEPER = 30695, + NPC_XEVOZZ = 29266, + NPC_LAVANTHOR = 29312, + NPC_ICHORON = 29313, + NPC_ZURAMAT = 29314, + NPC_EREKEM = 29315, + NPC_EREKEM_GUARD = 29395, + NPC_MORAGG = 29316, + NPC_CYANIGOSA = 31134, + NPC_SINCLARI = 30658, + NPC_SABOTEOUR = 31079, + NPC_VIOLET_HOLD_GUARD = 30659, + NPC_DEFENSE_SYSTEM = 30837, + NPC_VOID_SENTRY = 29364, + NPC_VOID_SENTRY_BALL = 29365 +}; + +enum GameObjectIds +{ + GO_MAIN_DOOR = 191723, + GO_XEVOZZ_DOOR = 191556, + GO_LAVANTHOR_DOOR = 191566, + GO_ICHORON_DOOR = 191722, + GO_ZURAMAT_DOOR = 191565, + GO_EREKEM_DOOR = 191564, + 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 }; enum WorldStateIds { - WORLD_STATE_VH = 3816, - WORLD_STATE_VH_PRISON_STATE = 3815, - WORLD_STATE_VH_WAVE_COUNT = 3810, + WORLD_STATE_VH = 3816, + WORLD_STATE_VH_PRISON_STATE = 3815, + WORLD_STATE_VH_WAVE_COUNT = 3810, }; enum Events { - EVENT_ACTIVATE_CRYSTAL = 20001 + EVENT_ACTIVATE_CRYSTAL = 20001 }; #endif diff --git a/src/server/scripts/Northrend/isle_of_conquest.cpp b/src/server/scripts/Northrend/isle_of_conquest.cpp index ba21b60f11d..f5514cc51a5 100644 --- a/src/server/scripts/Northrend/isle_of_conquest.cpp +++ b/src/server/scripts/Northrend/isle_of_conquest.cpp @@ -161,19 +161,6 @@ class spell_ioc_gunship_portal : public SpellScriptLoader * Position: X: 7.305609 Y: -0.095246 Z: 34.51022 O: 0 */ caster->TeleportTo(GetHitCreature()->GetWorldLocation(), TELE_TO_NOT_LEAVE_TRANSPORT); - /* - * HACK: This aura should be added by 20212 and 20213 but can't find any SMSG_SPELL_GO. Could't find their position. - * ServerToClient: SMSG_AURA_UPDATE (0x0072) - * [0] CasterGUID: Full: xxxxx Type: Unit Entry: 20212 Low: xxx - * [0] Flags: None (0) - * [0] Caster Level: 60 - * [0] Spell ID: 66656 - * [0] Charges: 0 - * [0] Effect Mask: 1 - * [0] Slot: 37 - * Guid: Full: xxxxx Type: Player2 Low: xxxxx - */ - caster->AddAura(SPELL_PARACHUTE, caster); } void Register() override diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPNA.h b/src/server/scripts/OutdoorPvP/OutdoorPvPNA.h index 80c8041bc6d..fac9d8fa357 100644 --- a/src/server/scripts/OutdoorPvP/OutdoorPvPNA.h +++ b/src/server/scripts/OutdoorPvP/OutdoorPvPNA.h @@ -261,13 +261,13 @@ class OPvPCapturePointNA : public OPvPCapturePoint public: OPvPCapturePointNA(OutdoorPvP* pvp); - bool Update(uint32 diff); + bool Update(uint32 diff) override; - void ChangeState(); + void ChangeState() override; - void FillInitialWorldStates(WorldPacket & data); + void FillInitialWorldStates(WorldPacket & data) override; - bool HandleCustomSpell(Player* player, uint32 spellId, GameObject* go); + bool HandleCustomSpell(Player* player, uint32 spellId, GameObject* go) override; int32 HandleOpenGo(Player* player, ObjectGuid guid) override; diff --git a/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp b/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp index 662bad46162..263fd8340b9 100644 --- a/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp +++ b/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp @@ -113,7 +113,7 @@ public: Map::PlayerList const &PlayerList = map->GetPlayers(); for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) if (Player* i_pl = i->GetSource()) - if (i_pl->IsAlive() && (dist = i_pl->IsWithinDist(me, 45))) + if (i_pl->IsAlive() && (dist = i_pl->GetDistance(me)) < 45) { i_pl->RemoveAurasDueToSpell(SPELL_INHIBITMAGIC); me->AddAura(SPELL_INHIBITMAGIC, i_pl); diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp index 81c1283768b..500b0ae4bcb 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp @@ -84,17 +84,13 @@ class boss_shattered_executioner : public CreatureScript _Reset(); // _Reset() resets the loot mode, so we add them again, if any - switch (instance->GetData(DATA_PRISONERS_EXECUTED)) - { - case 0: - me->AddLootMode(LOOT_MODE_HARD_MODE_3); - case 1: - me->AddLootMode(LOOT_MODE_HARD_MODE_2); - case 2: - me->AddLootMode(LOOT_MODE_HARD_MODE_1); - default: - break; - } + uint32 prisonersExecuted = instance->GetData(DATA_PRISONERS_EXECUTED); + if (prisonersExecuted == 0) + me->AddLootMode(LOOT_MODE_HARD_MODE_3); + if (prisonersExecuted <= 1) + me->AddLootMode(LOOT_MODE_HARD_MODE_2); + if (prisonersExecuted <= 2) + me->AddLootMode(LOOT_MODE_HARD_MODE_1); if (instance->GetBossState(DATA_KARGATH) == DONE) me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp index cc825afd3a0..48508aa4790 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp @@ -77,12 +77,11 @@ class boss_alar : public CreatureScript public: boss_alar() : CreatureScript("boss_alar") { } - struct boss_alarAI : public ScriptedAI + struct boss_alarAI : public BossAI { - boss_alarAI(Creature* creature) : ScriptedAI(creature) + boss_alarAI(Creature* creature) : BossAI(creature, DATA_ALAR) { Initialize(); - instance = creature->GetInstanceScript(); DefaultMoveSpeedRate = creature->GetSpeedRate(MOVE_RUN); DiveBomb_Timer = 0; MeltArmor_Timer = 0; @@ -105,8 +104,6 @@ class boss_alar : public CreatureScript cur_wp = 4; } - InstanceScript* instance; - WaitEventType WaitEvent; uint32 WaitTimer; @@ -129,9 +126,8 @@ class boss_alar : public CreatureScript void Reset() override { - instance->SetData(DATA_ALAREVENT, NOT_STARTED); - Initialize(); + _Reset(); me->SetDisplayId(me->GetNativeDisplayId()); me->SetSpeed(MOVE_RUN, DefaultMoveSpeedRate); @@ -145,18 +141,11 @@ class boss_alar : public CreatureScript void EnterCombat(Unit* /*who*/) override { - instance->SetData(DATA_ALAREVENT, IN_PROGRESS); - + _EnterCombat(); me->SetDisableGravity(true); // after enterevademode will be set walk movement - DoZoneInCombat(); me->setActive(true); } - void JustDied(Unit* /*killer*/) override - { - instance->SetData(DATA_ALAREVENT, DONE); - } - void JustSummoned(Creature* summon) override { if (summon->GetEntry() == CREATURE_EMBER_OF_ALAR) @@ -508,7 +497,7 @@ class npc_ember_of_alar : public CreatureScript DoCast(me, SPELL_EMBER_BLAST, true); me->SetDisplayId(11686); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - if (instance->GetData(DATA_ALAREVENT) == 2) + if (instance->GetBossState(DATA_ALAR) == IN_PROGRESS) { if (Unit* Alar = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_ALAR))) { diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp index 6021e38d3cf..ff9bdf15276 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp @@ -92,17 +92,13 @@ class boss_high_astromancer_solarian : public CreatureScript { public: - boss_high_astromancer_solarian() - : CreatureScript("boss_high_astromancer_solarian") - { - } + boss_high_astromancer_solarian() : CreatureScript("boss_high_astromancer_solarian") { } - struct boss_high_astromancer_solarianAI : public ScriptedAI + struct boss_high_astromancer_solarianAI : public BossAI { - boss_high_astromancer_solarianAI(Creature* creature) : ScriptedAI(creature), Summons(me) + boss_high_astromancer_solarianAI(Creature* creature) : BossAI(creature, DATA_HIGH_ASTROMANCER_SOLARIAN) { Initialize(); - instance = creature->GetInstanceScript(); defaultarmor = creature->GetArmor(); defaultsize = creature->GetObjectScale(); @@ -126,9 +122,6 @@ class boss_high_astromancer_solarian : public CreatureScript Phase = 1; } - InstanceScript* instance; - SummonList Summons; - uint8 Phase; uint32 ArcaneMissiles_Timer; @@ -152,16 +145,13 @@ class boss_high_astromancer_solarian : public CreatureScript void Reset() override { Initialize(); - - instance->SetData(DATA_HIGHASTROMANCERSOLARIANEVENT, NOT_STARTED); - + _Reset(); me->SetArmor(defaultarmor); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->SetVisible(true); me->SetObjectScale(defaultsize); me->SetDisplayId(MODEL_HUMAN); - Summons.DespawnAll(); } void KilledUnit(Unit* /*victim*/) override @@ -174,15 +164,13 @@ class boss_high_astromancer_solarian : public CreatureScript me->SetObjectScale(defaultsize); me->SetDisplayId(MODEL_HUMAN); Talk(SAY_DEATH); - instance->SetData(DATA_HIGHASTROMANCERSOLARIANEVENT, DONE); + _JustDied(); } void EnterCombat(Unit* /*who*/) override { Talk(SAY_AGGRO); - DoZoneInCombat(); - - instance->SetData(DATA_HIGHASTROMANCERSOLARIANEVENT, IN_PROGRESS); + _EnterCombat(); } void SummonMinion(uint32 entry, float x, float y, float z) @@ -193,7 +181,7 @@ class boss_high_astromancer_solarian : public CreatureScript if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) Summoned->AI()->AttackStart(target); - Summons.Summon(Summoned); + summons.Summon(Summoned); } } @@ -431,10 +419,7 @@ class npc_solarium_priest : public CreatureScript { public: - npc_solarium_priest() - : CreatureScript("npc_solarium_priest") - { - } + npc_solarium_priest() : CreatureScript("npc_solarium_priest") { } struct npc_solarium_priestAI : public ScriptedAI { @@ -462,9 +447,7 @@ class npc_solarium_priest : public CreatureScript Initialize(); } - void EnterCombat(Unit* /*who*/) override - { - } + void EnterCombat(Unit* /*who*/) override { } void UpdateAI(uint32 diff) override { diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index 9aa1981eabb..f5583c0bc53 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -286,17 +286,13 @@ class boss_kaelthas : public CreatureScript { public: - boss_kaelthas() - : CreatureScript("boss_kaelthas") - { - } - //Kael'thas AI - struct boss_kaelthasAI : public ScriptedAI + boss_kaelthas() : CreatureScript("boss_kaelthas") { } + + struct boss_kaelthasAI : public BossAI { - boss_kaelthasAI(Creature* creature) : ScriptedAI(creature), summons(me) + boss_kaelthasAI(Creature* creature) : BossAI(creature, DATA_KAELTHAS) { Initialize(); - instance = creature->GetInstanceScript(); PhaseSubphase = 0; Phase_Timer = 0; } @@ -320,8 +316,6 @@ class boss_kaelthas : public CreatureScript ChainPyros = false; } - InstanceScript* instance; - uint32 Fireball_Timer; uint32 ArcaneDisruption_Timer; uint32 Phoenix_Timer; @@ -341,8 +335,6 @@ class boss_kaelthas : public CreatureScript bool IsCastingFireball; bool ChainPyros; - SummonList summons; - ObjectGuid m_auiAdvisorGuid[MAX_ADVISORS]; void Reset() override @@ -351,8 +343,8 @@ class boss_kaelthas : public CreatureScript if (me->IsInCombat()) PrepareAdvisors(); - - summons.DespawnAll(); + + _Reset(); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); @@ -413,7 +405,6 @@ class boss_kaelthas : public CreatureScript } void MoveInLineOfSight(Unit* who) override - { if (!me->HasUnitState(UNIT_STATE_STUNNED) && me->CanCreatureAttack(who)) { @@ -444,6 +435,8 @@ class boss_kaelthas : public CreatureScript { if (!instance->GetData(DATA_KAELTHASEVENT) && !Phase) StartEvent(); + + instance->SetBossState(DATA_KAELTHAS, IN_PROGRESS); } void KilledUnit(Unit* /*victim*/) override @@ -463,11 +456,6 @@ class boss_kaelthas : public CreatureScript } } - void SummonedCreatureDespawn(Creature* summon) override - { - summons.Despawn(summon); - } - void JustDied(Unit* /*killer*/) override { me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); @@ -475,8 +463,6 @@ class boss_kaelthas : public CreatureScript Talk(SAY_DEATH); - summons.DespawnAll(); - instance->SetData(DATA_KAELTHASEVENT, 0); for (uint8 i = 0; i < MAX_ADVISORS; ++i) @@ -484,6 +470,7 @@ class boss_kaelthas : public CreatureScript if (Unit* pAdvisor = ObjectAccessor::GetUnit(*me, m_auiAdvisorGuid[i])) pAdvisor->Kill(pAdvisor); } + _JustDied(); } void UpdateAI(uint32 diff) override @@ -1024,10 +1011,8 @@ class boss_thaladred_the_darkener : public CreatureScript { public: - boss_thaladred_the_darkener() - : CreatureScript("boss_thaladred_the_darkener") - { - } + boss_thaladred_the_darkener() : CreatureScript("boss_thaladred_the_darkener") { } + struct boss_thaladred_the_darkenerAI : public advisorbase_ai { boss_thaladred_the_darkenerAI(Creature* creature) : advisorbase_ai(creature) @@ -1130,10 +1115,8 @@ class boss_lord_sanguinar : public CreatureScript { public: - boss_lord_sanguinar() - : CreatureScript("boss_lord_sanguinar") - { - } + boss_lord_sanguinar() : CreatureScript("boss_lord_sanguinar") { } + struct boss_lord_sanguinarAI : public advisorbase_ai { boss_lord_sanguinarAI(Creature* creature) : advisorbase_ai(creature) @@ -1205,10 +1188,8 @@ class boss_grand_astromancer_capernian : public CreatureScript { public: - boss_grand_astromancer_capernian() - : CreatureScript("boss_grand_astromancer_capernian") - { - } + boss_grand_astromancer_capernian() : CreatureScript("boss_grand_astromancer_capernian") { } + struct boss_grand_astromancer_capernianAI : public advisorbase_ai { boss_grand_astromancer_capernianAI(Creature* creature) : advisorbase_ai(creature) @@ -1358,10 +1339,8 @@ class boss_master_engineer_telonicus : public CreatureScript { public: - boss_master_engineer_telonicus() - : CreatureScript("boss_master_engineer_telonicus") - { - } + boss_master_engineer_telonicus() : CreatureScript("boss_master_engineer_telonicus") { } + struct boss_master_engineer_telonicusAI : public advisorbase_ai { boss_master_engineer_telonicusAI(Creature* creature) : advisorbase_ai(creature) @@ -1449,10 +1428,8 @@ class npc_kael_flamestrike : public CreatureScript { public: - npc_kael_flamestrike() - : CreatureScript("npc_kael_flamestrike") - { - } + npc_kael_flamestrike() : CreatureScript("npc_kael_flamestrike") { } + struct npc_kael_flamestrikeAI : public ScriptedAI { npc_kael_flamestrikeAI(Creature* creature) : ScriptedAI(creature) @@ -1482,7 +1459,6 @@ class npc_kael_flamestrike : public CreatureScript void MoveInLineOfSight(Unit* /*who*/) override { } - void EnterCombat(Unit* /*who*/) override { } void UpdateAI(uint32 diff) override @@ -1523,10 +1499,8 @@ class npc_phoenix_tk : public CreatureScript { public: - npc_phoenix_tk() - : CreatureScript("npc_phoenix_tk") - { - } + npc_phoenix_tk() : CreatureScript("npc_phoenix_tk") { } + struct npc_phoenix_tkAI : public ScriptedAI { npc_phoenix_tkAI(Creature* creature) : ScriptedAI(creature) @@ -1585,10 +1559,8 @@ class npc_phoenix_egg_tk : public CreatureScript { public: - npc_phoenix_egg_tk() - : CreatureScript("npc_phoenix_egg_tk") - { - } + npc_phoenix_egg_tk() : CreatureScript("npc_phoenix_egg_tk") { } + struct npc_phoenix_egg_tkAI : public ScriptedAI { npc_phoenix_egg_tkAI(Creature* creature) : ScriptedAI(creature) @@ -1611,7 +1583,6 @@ class npc_phoenix_egg_tk : public CreatureScript //ignore any void MoveInLineOfSight(Unit* /*who*/) override { } - void AttackStart(Unit* who) override { if (me->Attack(who, false)) diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp index 9186b491fa4..1aa7d5298e5 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp @@ -47,17 +47,13 @@ class boss_void_reaver : public CreatureScript { public: - boss_void_reaver() - : CreatureScript("boss_void_reaver") - { - } + boss_void_reaver() : CreatureScript("boss_void_reaver") { } - struct boss_void_reaverAI : public ScriptedAI + struct boss_void_reaverAI : public BossAI { - boss_void_reaverAI(Creature* creature) : ScriptedAI(creature) + boss_void_reaverAI(Creature* creature) : BossAI(creature, DATA_VOID_REAVER) { Initialize(); - instance = creature->GetInstanceScript(); } void Initialize() @@ -70,8 +66,6 @@ class boss_void_reaver : public CreatureScript Enraged = false; } - InstanceScript* instance; - uint32 Pounding_Timer; uint32 ArcaneOrb_Timer; uint32 KnockAway_Timer; @@ -82,9 +76,7 @@ class boss_void_reaver : public CreatureScript void Reset() override { Initialize(); - - if (me->IsAlive()) - instance->SetData(DATA_VOIDREAVEREVENT, NOT_STARTED); + _Reset(); } void KilledUnit(Unit* /*victim*/) override @@ -96,15 +88,13 @@ class boss_void_reaver : public CreatureScript { Talk(SAY_DEATH); DoZoneInCombat(); - - instance->SetData(DATA_VOIDREAVEREVENT, DONE); + _JustDied(); } void EnterCombat(Unit* /*who*/) override { Talk(SAY_AGGRO); - - instance->SetData(DATA_VOIDREAVEREVENT, IN_PROGRESS); + _EnterCombat(); } void UpdateAI(uint32 diff) override diff --git a/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp b/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp index 38c9cdd487c..c421b9974ce 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp @@ -27,8 +27,6 @@ EndScriptData */ #include "InstanceScript.h" #include "the_eye.h" -#define MAX_ENCOUNTER 5 - /* The Eye encounters: 0 - Kael'thas event 1 - Al' ar event @@ -39,20 +37,16 @@ EndScriptData */ class instance_the_eye : public InstanceMapScript { public: - instance_the_eye() - : InstanceMapScript("instance_the_eye", 550) - { - } + instance_the_eye() : InstanceMapScript("instance_the_eye", 550) { } struct instance_the_eye_InstanceMapScript : public InstanceScript { instance_the_eye_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); - memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); + SetBossNumber(EncounterCount); KaelthasEventPhase = 0; - AlarEventPhase = 0; } ObjectGuid ThaladredTheDarkener; @@ -63,44 +57,34 @@ class instance_the_eye : public InstanceMapScript ObjectGuid Astromancer; ObjectGuid Alar; uint8 KaelthasEventPhase; - uint8 AlarEventPhase; - - uint32 m_auiEncounter[MAX_ENCOUNTER]; - - bool IsEncounterInProgress() const override - { - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (m_auiEncounter[i] == IN_PROGRESS) - return true; - - return false; - } void OnCreatureCreate(Creature* creature) override { switch (creature->GetEntry()) { - case 20064: - ThaladredTheDarkener = creature->GetGUID(); - break; - case 20063: - MasterEngineerTelonicus = creature->GetGUID(); - break; - case 20062: - GrandAstromancerCapernian = creature->GetGUID(); - break; - case 20060: - LordSanguinar = creature->GetGUID(); - break; - case 19622: - Kaelthas = creature->GetGUID(); - break; - case 18805: - Astromancer = creature->GetGUID(); - break; - case 19514: - Alar = creature->GetGUID(); - break; + case NPC_SANGUINAR: + LordSanguinar = creature->GetGUID(); + break; + case NPC_CAPERNIAN: + GrandAstromancerCapernian = creature->GetGUID(); + break; + case NPC_TELONICUS: + MasterEngineerTelonicus = creature->GetGUID(); + break; + case NPC_THALADRED: + ThaladredTheDarkener = creature->GetGUID(); + break; + case NPC_KAELTHAS: + Kaelthas = creature->GetGUID(); + break; + case NPC_HIGH_ASTROMANCER_SOLARIAN: + Astromancer = creature->GetGUID(); + break; + case NPC_ALAR: + Alar = creature->GetGUID(); + break; + default: + break; } } @@ -113,7 +97,7 @@ class instance_the_eye : public InstanceMapScript case DATA_GRANDASTROMANCERCAPERNIAN: return GrandAstromancerCapernian; case DATA_MASTERENGINEERTELONICUS: return MasterEngineerTelonicus; case DATA_KAELTHAS: return Kaelthas; - case DATA_ASTROMANCER: return Astromancer; + case DATA_HIGH_ASTROMANCER_SOLARIAN: return Astromancer; case DATA_ALAR: return Alar; } return ObjectGuid::Empty; @@ -123,64 +107,23 @@ class instance_the_eye : public InstanceMapScript { switch (type) { - case DATA_ALAREVENT: - AlarEventPhase = data; - m_auiEncounter[0] = data; - break; - case DATA_HIGHASTROMANCERSOLARIANEVENT: - m_auiEncounter[1] = data; - break; - case DATA_VOIDREAVEREVENT: - m_auiEncounter[2] = data; - break; - case DATA_KAELTHASEVENT: - KaelthasEventPhase = data; - m_auiEncounter[3] = data; - break; + case DATA_KAELTHASEVENT: + KaelthasEventPhase = data; + break; + default: + break; } - if (data == DONE) - SaveToDB(); } uint32 GetData(uint32 type) const override { switch (type) { - case DATA_ALAREVENT: return AlarEventPhase; - case DATA_HIGHASTROMANCERSOLARIANEVENT: return m_auiEncounter[1]; - case DATA_VOIDREAVEREVENT: return m_auiEncounter[2]; - case DATA_KAELTHASEVENT: return KaelthasEventPhase; + case DATA_KAELTHASEVENT: + return KaelthasEventPhase; } return 0; } - - std::string GetSaveData() override - { - OUT_SAVE_INST_DATA; - - std::ostringstream stream; - stream << m_auiEncounter[0] << ' ' << m_auiEncounter[1] << ' ' << m_auiEncounter[2] << ' ' << m_auiEncounter[3]; - - OUT_SAVE_INST_DATA_COMPLETE; - return stream.str(); - } - - void Load(const char* in) override - { - if (!in) - { - OUT_LOAD_INST_DATA_FAIL; - return; - } - OUT_LOAD_INST_DATA(in); - - std::istringstream stream(in); - stream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3]; - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (m_auiEncounter[i] == IN_PROGRESS) // Do not load an encounter as "In Progress" - reset it instead. - m_auiEncounter[i] = NOT_STARTED; - OUT_LOAD_INST_DATA_COMPLETE; - } }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h b/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h index 718f37861d9..c46fe408274 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h +++ b/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h @@ -21,19 +21,33 @@ #define DataHeader "TE" +uint32 const EncounterCount = 4; + enum DataTypes { - DATA_ALAREVENT = 1, - DATA_ASTROMANCER = 2, - DATA_GRANDASTROMANCERCAPERNIAN = 3, - DATA_HIGHASTROMANCERSOLARIANEVENT = 4, - DATA_KAELTHAS = 5, + // Encounter States/Boss GUIDs + DATA_KAELTHAS = 0, + DATA_ALAR = 1, + DATA_HIGH_ASTROMANCER_SOLARIAN = 2, + DATA_VOID_REAVER = 3, + + DATA_ASTROMANCER = 4, + DATA_GRANDASTROMANCERCAPERNIAN = 5, DATA_KAELTHASEVENT = 6, DATA_LORDSANGUINAR = 7, DATA_MASTERENGINEERTELONICUS = 8, - DATA_THALADREDTHEDARKENER = 10, - DATA_VOIDREAVEREVENT = 11, - DATA_ALAR = 12 + DATA_THALADREDTHEDARKENER = 9 +}; + +enum CreatureIds +{ + NPC_SANGUINAR = 20060, + NPC_CAPERNIAN = 20062, + NPC_TELONICUS = 20063, + NPC_THALADRED = 20064, + NPC_KAELTHAS = 19622, + NPC_HIGH_ASTROMANCER_SOLARIAN = 18805, + NPC_ALAR = 19514 }; #endif diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index 471c776d54b..d1a31906d58 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -770,7 +770,8 @@ public: void Initialize() { circleRounds = 0; - point = 0; + point = 3; + wpreached = false; } void Reset() override @@ -778,9 +779,7 @@ public: events.Reset(); summons.DespawnAll(); - circleRounds = 0; - point = 3; - wpreached = false; + Initialize(); } void DoAction(int32 action) override diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp index 93e13667cb0..a1b218010aa 100644 --- a/src/server/scripts/Spells/spell_holiday.cpp +++ b/src/server/scripts/Spells/spell_holiday.cpp @@ -824,6 +824,60 @@ class spell_brewfest_barker_bunny : public SpellScriptLoader } }; +enum TorchSpells +{ + SPELL_TORCH_TOSSING_TRAINING = 45716, + SPELL_TORCH_TOSSING_PRACTICE = 46630, + SPELL_TORCH_TOSSING_TRAINING_SUCCESS_ALLIANCE = 45719, + SPELL_TORCH_TOSSING_TRAINING_SUCCESS_HORDE = 46651, + SPELL_BRAZIERS_HIT = 45724 +}; + +// 45724 - Braziers Hit! +class spell_midsummer_braziers_hit : public SpellScriptLoader +{ + public: + spell_midsummer_braziers_hit() : SpellScriptLoader("spell_midsummer_braziers_hit") { } + + class spell_midsummer_braziers_hit_AuraScript : public AuraScript + { + PrepareAuraScript(spell_midsummer_braziers_hit_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_TORCH_TOSSING_TRAINING) || !sSpellMgr->GetSpellInfo(SPELL_TORCH_TOSSING_PRACTICE)) + return false; + return true; + } + + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Player* player = GetTarget()->ToPlayer(); + if (!player) + return; + + if ((player->HasAura(SPELL_TORCH_TOSSING_TRAINING) && GetStackAmount() == 8) || (player->HasAura(SPELL_TORCH_TOSSING_PRACTICE) && GetStackAmount() == 20)) + { + if (player->GetTeam() == ALLIANCE) + player->CastSpell(player, SPELL_TORCH_TOSSING_TRAINING_SUCCESS_ALLIANCE, true); + else if (player->GetTeam() == HORDE) + player->CastSpell(player, SPELL_TORCH_TOSSING_TRAINING_SUCCESS_HORDE, true); + Remove(); + } + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_midsummer_braziers_hit_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AuraEffectHandleModes(AURA_EFFECT_HANDLE_REAPPLY)); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_midsummer_braziers_hit_AuraScript(); + } +}; + void AddSC_holiday_spell_scripts() { // Love is in the Air @@ -850,4 +904,6 @@ void AddSC_holiday_spell_scripts() new spell_brewfest_relay_race_intro_force_player_to_throw(); new spell_brewfest_dismount_ram(); new spell_brewfest_barker_bunny(); + // Midsummer Fire Festival + new spell_midsummer_braziers_hit(); } diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index 0ec8bfc3a9c..60ff67d74c0 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -339,7 +339,7 @@ class spell_q11396_11399_scourging_crystal_controller : public SpellScriptLoader SpellScript* GetSpellScript() const override { return new spell_q11396_11399_scourging_crystal_controller_SpellScript(); - }; + } }; // 43882 Scourging Crystal Controller Dummy @@ -375,7 +375,7 @@ class spell_q11396_11399_scourging_crystal_controller_dummy : public SpellScript SpellScript* GetSpellScript() const override { return new spell_q11396_11399_scourging_crystal_controller_dummy_SpellScript(); - }; + } }; // http://www.wowhead.com/quest=11515 Blood for Blood @@ -856,7 +856,7 @@ class spell_symbol_of_life_dummy : public SpellScriptLoader SpellScript* GetSpellScript() const override { return new spell_symbol_of_life_dummy_SpellScript(); - }; + } }; // http://www.wowhead.com/quest=12659 Scalps! @@ -899,7 +899,7 @@ class spell_q12659_ahunaes_knife : public SpellScriptLoader SpellScript* GetSpellScript() const override { return new spell_q12659_ahunaes_knife_SpellScript(); - }; + } }; enum StoppingTheSpread @@ -945,7 +945,7 @@ class spell_q9874_liquid_fire : public SpellScriptLoader SpellScript* GetSpellScript() const override { return new spell_q9874_liquid_fire_SpellScript(); - }; + } }; enum SalvagingLifesStength @@ -989,7 +989,7 @@ class spell_q12805_lifeblood_dummy : public SpellScriptLoader SpellScript* GetSpellScript() const override { return new spell_q12805_lifeblood_dummy_SpellScript(); - }; + } }; /* diff --git a/src/server/shared/Cryptography/SHA1.h b/src/server/shared/Cryptography/SHA1.h index ebd9f721d4a..f59bdc25556 100644 --- a/src/server/shared/Cryptography/SHA1.h +++ b/src/server/shared/Cryptography/SHA1.h @@ -39,8 +39,8 @@ class SHA1Hash void Initialize(); void Finalize(); - uint8 *GetDigest(void) { return mDigest; }; - int GetLength(void) const { return SHA_DIGEST_LENGTH; }; + uint8 *GetDigest(void) { return mDigest; } + int GetLength(void) const { return SHA_DIGEST_LENGTH; } private: SHA_CTX mC; diff --git a/src/server/shared/Database/DatabaseLoader.cpp b/src/server/shared/Database/DatabaseLoader.cpp index 36ee4b12c83..a3e2083fff4 100644 --- a/src/server/shared/Database/DatabaseLoader.cpp +++ b/src/server/shared/Database/DatabaseLoader.cpp @@ -117,6 +117,9 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st bool DatabaseLoader::Load() { + if (!_updateFlags) + TC_LOG_INFO("sql.updates", "Automatic database updates are disabled for all databases!"); + if (!OpenDatabases()) return false; diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 1ca01501d01..1efdb16804f 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -68,13 +68,13 @@ void CharacterDatabaseConnection::DoPrepareStatements() "position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, " "resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, " "arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, " - "health, power1, power2, power3, power4, power5, power6, power7, instance_id, speccount, activespec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels " + "health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels " "FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH); PrepareStatement(CHAR_SEL_CHARACTER_INSTANCE, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT caster_guid, spell, effect_mask, recalculate_mask, stackcount, amount0, amount1, amount2, " - "base_amount0, base_amount1, base_amount2, maxduration, remaintime, remaincharges FROM character_aura WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, " + "base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM character_aura WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_SPELL, "SELECT spell, active, disabled FROM character_spell WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS, "SELECT quest, status, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, " "itemcount1, itemcount2, itemcount3, itemcount4, playercount FROM character_queststatus WHERE guid = ? AND status <> 0", CONNECTION_ASYNC); @@ -99,7 +99,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, bag, slot, " "item, itemEntry FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activespec AND a.guid = ? ORDER BY button", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activeTalentGroup AND a.guid = ? ORDER BY button", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_MAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = ? AND (checked & 1) = 0", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?", CONNECTION_SYNCH); @@ -116,8 +116,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER_EQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, ignore_mask, item0, item1, item2, item3, item4, item5, item6, item7, item8, " "item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = ? ORDER BY setindex", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_BGDATA, "SELECT instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell FROM character_battleground_data WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_GLYPHS, "SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_TALENTS, "SELECT spell, spec FROM character_talent WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_GLYPHS, "SELECT talentGroup, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_TALENTS, "SELECT spell, talentGroup FROM character_talent WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_SKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_RANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_BANNED, "SELECT guid FROM character_banned WHERE guid = ? AND active = 1", CONNECTION_ASYNC); @@ -247,7 +247,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_EQUIP_SET, "DELETE FROM character_equipmentsets WHERE setguid=?", CONNECTION_ASYNC); // Auras - PrepareStatement(CHAR_INS_AURA, "INSERT INTO character_aura (guid, caster_guid, item_guid, spell, effect_mask, recalculate_mask, stackcount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxduration, remaintime, remaincharges) " + PrepareStatement(CHAR_INS_AURA, "INSERT INTO character_aura (guid, casterGuid, itemGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); // Account data @@ -336,14 +336,14 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_GO_RESPAWN_BY_INSTANCE, "DELETE FROM gameobject_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC); // GM Tickets - PrepareStatement(CHAR_SEL_GM_TICKETS, "SELECT ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, haveTicket FROM gm_tickets", CONNECTION_SYNCH); - PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_tickets (ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, haveTicket) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_GM_TICKET, "DELETE FROM gm_tickets WHERE ticketId = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_PLAYER_GM_TICKETS, "DELETE FROM gm_tickets WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_GM_TICKETS, "SELECT id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp FROM gm_ticket", CONNECTION_SYNCH); + PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_ticket (id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_GM_TICKET, "DELETE FROM gm_ticket WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PLAYER_GM_TICKETS, "DELETE FROM gm_ticket WHERE playerGuid = ?", CONNECTION_ASYNC); // GM Survey/subsurvey/lag report - PrepareStatement(CHAR_INS_GM_SURVEY, "INSERT INTO gm_surveys (guid, surveyId, mainSurvey, overallComment, createTime) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(NOW()))", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_GM_SUBSURVEY, "INSERT INTO gm_subsurveys (surveyId, subsurveyId, rank, comment) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_GM_SURVEY, "INSERT INTO gm_survey (guid, surveyId, mainSurvey, comment, createTime) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(NOW()))", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_GM_SUBSURVEY, "INSERT INTO gm_subsurvey (surveyId, questionId, answer, answerComment) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_LAG_REPORT, "INSERT INTO lag_reports (guid, lagType, mapId, posX, posY, posZ, latency, createTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); // LFG Data @@ -358,13 +358,13 @@ void CharacterDatabaseConnection::DoPrepareStatements() "extra_flags, stable_slots, at_login, zone, " "death_expire_time, taxi_path, arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, " "todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, health, power1, power2, power3, " - "power4, power5, power6, power7, latency, speccount, activespec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels) VALUES " + "power4, power5, power6, power7, latency, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels) VALUES " "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,playerBytes=?,playerBytes2=?,playerFlags=?," "map=?,instance_id=?,instance_mode_mask=?,position_x=?,position_y=?,position_z=?,orientation=?,trans_x=?,trans_y=?,trans_z=?,trans_o=?,transguid=?,taximask=?,cinematic=?,totaltime=?,leveltime=?,rest_bonus=?," "logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?," "arenaPoints=?,totalHonorPoints=?,todayHonorPoints=?,yesterdayHonorPoints=?,totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?,knownCurrencies=?," - "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,power6=?,power7=?,latency=?,speccount=?,activespec=?,exploredZones=?," + "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,power6=?,power7=?,latency=?,talentGroupsCount=?,activeTalentGroup=?,exploredZones=?," "equipmentCache=?,ammoId=?,knownTitles=?,actionBars=?,grantableLevels=?,online=? WHERE guid=?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG, "UPDATE characters SET at_login = at_login | ? WHERE guid = ?", CONNECTION_ASYNC); @@ -374,7 +374,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petitionguid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petitionguid, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ACCOUNT_ONLINE, "UPDATE characters SET online = 0 WHERE account = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_GROUP, "INSERT INTO groups (guid, leaderGuid, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raiddifficulty, masterLooterGuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_GROUP, "INSERT INTO groups (guid, leaderGuid, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raidDifficulty, masterLooterGuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_GROUP_MEMBER, "INSERT INTO group_member (guid, memberGuid, memberFlags, subgroup, roles) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_GROUP_MEMBER, "DELETE FROM group_member WHERE memberGuid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_GROUP_INSTANCE_PERM_BINDING, "DELETE FROM group_instance WHERE guid = ? AND instance = ?", CONNECTION_ASYNC); @@ -384,7 +384,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_GROUP_MEMBER_FLAG, "UPDATE group_member SET memberFlags = ? WHERE memberGuid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GROUP_DIFFICULTY, "UPDATE groups SET difficulty = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GROUP_RAID_DIFFICULTY, "UPDATE groups SET raiddifficulty = ? WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_ALL_GM_TICKETS, "TRUNCATE TABLE gm_tickets", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_ALL_GM_TICKETS, "TRUNCATE TABLE gm_ticket", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INVALID_SPELL_TALENTS, "DELETE FROM character_talent WHERE spell = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INVALID_SPELL_SPELLS, "DELETE FROM character_spell WHERE spell = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_DELETE_INFO, "UPDATE characters SET deleteInfos_Name = name, deleteInfos_Account = account, deleteDate = UNIX_TIMESTAMP(), name = '', account = 0 WHERE guid = ?", CONNECTION_ASYNC); @@ -415,7 +415,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHARACTER_SOCIAL, "DELETE FROM character_social WHERE guid = ? AND friend = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHARACTER_SOCIAL_NOTE, "UPDATE character_social SET note = ? WHERE guid = ? AND friend = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHARACTER_POSITION, "UPDATE characters SET position_x = ?, position_y = ?, position_z = ?, orientation = ?, map = ?, zone = ?, trans_x = 0, trans_y = 0, trans_z = 0, transguid = 0, taxi_path = '' WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_AURA_FROZEN, "SELECT characters.name, character_aura.remaintime FROM characters LEFT JOIN character_aura ON (characters.guid = character_aura.guid) WHERE character_aura.spell = 9454", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_CHARACTER_AURA_FROZEN, "SELECT characters.name, character_aura.remainTime FROM characters LEFT JOIN character_aura ON (characters.guid = character_aura.guid) WHERE character_aura.spell = 9454", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHARACTER_ONLINE, "SELECT name, account, map, zone FROM characters WHERE online > 0", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_NAME, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND deleteInfos_Name LIKE CONCAT('%%', ?, '%%')", CONNECTION_SYNCH); @@ -546,8 +546,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_PETITION_BY_OWNER_AND_TYPE, "DELETE FROM petition WHERE ownerguid = ? AND type = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER_AND_TYPE, "DELETE FROM petition_sign WHERE ownerguid = ? AND type = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHAR_GLYPHS, "INSERT INTO character_glyphs VALUES(?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC, "DELETE FROM character_talent WHERE guid = ? and spell = ? and spec = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_CHAR_TALENT, "INSERT INTO character_talent (guid, spell, spec) VALUES (?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC, "DELETE FROM character_talent WHERE guid = ? AND spell = ? AND talentGroup = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHAR_TALENT, "INSERT INTO character_talent (guid, spell, talentGroup) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, "DELETE FROM character_action WHERE spec<>? AND guid = ?", CONNECTION_ASYNC); // Items that hold loot or money @@ -576,7 +576,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, "DELETE FROM character_pet_declinedname WHERE owner = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME, "DELETE FROM character_pet_declinedname WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_PET_AURA, "SELECT caster_guid, spell, effect_mask, recalculate_mask, stackcount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxduration, remaintime, remaincharges FROM pet_aura WHERE guid = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time FROM pet_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = ? AND id = ?", CONNECTION_SYNCH); @@ -586,8 +586,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, time) VALUES (?, ?, ?)", CONNECTION_BOTH); PrepareStatement(CHAR_DEL_PET_SPELL_BY_SPELL, "DELETE FROM pet_spell WHERE guid = ? and spell = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_BOTH); - PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, caster_guid, spell, effect_mask, recalculate_mask, stackcount, amount0, amount1, amount2, " - "base_amount0, base_amount1, base_amount2, maxduration, remaintime, remaincharges) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH); + PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, " + "base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH); PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_SYNCH); diff --git a/src/server/shared/Database/Implementation/WorldDatabase.cpp b/src/server/shared/Database/Implementation/WorldDatabase.cpp index 4532f87d406..d9aad94293e 100644 --- a/src/server/shared/Database/Implementation/WorldDatabase.cpp +++ b/src/server/shared/Database/Implementation/WorldDatabase.cpp @@ -76,7 +76,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, permission, help FROM command", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, scale, rank, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, scale, rank, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH); diff --git a/src/server/shared/Debugging/WheatyExceptionReport.cpp b/src/server/shared/Debugging/WheatyExceptionReport.cpp index e50cf42e439..e9f888f280d 100644 --- a/src/server/shared/Debugging/WheatyExceptionReport.cpp +++ b/src/server/shared/Debugging/WheatyExceptionReport.cpp @@ -62,6 +62,8 @@ HANDLE WheatyExceptionReport::m_hProcess; SymbolPairs WheatyExceptionReport::symbols; std::stack<SymbolDetail> WheatyExceptionReport::symbolDetails; bool WheatyExceptionReport::stackOverflowException; +bool WheatyExceptionReport::alreadyCrashed; +std::mutex WheatyExceptionReport::alreadyCrashedLock; // Declare global instance of class WheatyExceptionReport g_WheatyExceptionReport; @@ -74,6 +76,7 @@ WheatyExceptionReport::WheatyExceptionReport() // Constructor m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter); m_hProcess = GetCurrentProcess(); stackOverflowException = false; + alreadyCrashed = false; if (!IsDebuggerPresent()) { _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); @@ -99,6 +102,13 @@ WheatyExceptionReport::~WheatyExceptionReport() LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter( PEXCEPTION_POINTERS pExceptionInfo) { + std::unique_lock<std::mutex> guard(alreadyCrashedLock); + // Handle only 1 exception in the whole process lifetime + if (alreadyCrashed) + return EXCEPTION_EXECUTE_HANDLER; + + alreadyCrashed = true; + if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW) stackOverflowException = true; diff --git a/src/server/shared/Debugging/WheatyExceptionReport.h b/src/server/shared/Debugging/WheatyExceptionReport.h index 101b6187f2b..ef6334add16 100644 --- a/src/server/shared/Debugging/WheatyExceptionReport.h +++ b/src/server/shared/Debugging/WheatyExceptionReport.h @@ -194,6 +194,8 @@ class WheatyExceptionReport static SymbolPairs symbols; static std::stack<SymbolDetail> symbolDetails; static bool stackOverflowException; + static bool alreadyCrashed; + static std::mutex alreadyCrashedLock; static char* PushSymbolDetail(char* pszCurrBuffer); static char* PopSymbolDetail(char* pszCurrBuffer); diff --git a/src/server/shared/PrecompiledHeaders/sharedPCH.h b/src/server/shared/PrecompiledHeaders/sharedPCH.h index d0c15b17f0c..87af9f44eb7 100644 --- a/src/server/shared/PrecompiledHeaders/sharedPCH.h +++ b/src/server/shared/PrecompiledHeaders/sharedPCH.h @@ -6,3 +6,4 @@ #include "SQLOperation.h" #include "Errors.h" #include "TypeList.h" +#include "TaskScheduler.h" diff --git a/src/server/shared/Threading/Callback.h b/src/server/shared/Threading/Callback.h index d0c80efb00a..1f4ffc97cfc 100644 --- a/src/server/shared/Threading/Callback.h +++ b/src/server/shared/Threading/Callback.h @@ -97,7 +97,7 @@ class QueryCallback //! Resets all underlying variables (param, result and stage) void Reset() { - SetParam(NULL); + SetParam(ParamType()); FreeResult(); ResetStage(); } diff --git a/src/server/shared/Updater/DBUpdater.cpp b/src/server/shared/Updater/DBUpdater.cpp index 20ded669cec..7b39062222e 100644 --- a/src/server/shared/Updater/DBUpdater.cpp +++ b/src/server/shared/Updater/DBUpdater.cpp @@ -239,10 +239,10 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool) [&](Path const& file) { DBUpdater<T>::ApplyFile(pool, file); }, [&](std::string const& query) -> QueryResult { return DBUpdater<T>::Retrieve(pool, query); }); - uint32 count; + UpdateResult result; try { - count = updateFetcher.Update( + result = updateFetcher.Update( sConfigMgr->GetBoolDefault("Updates.Redundancy", true), sConfigMgr->GetBoolDefault("Updates.AllowRehash", true), sConfigMgr->GetBoolDefault("Updates.ArchivedRedundancy", false), @@ -253,10 +253,13 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool) return false; } - if (!count) - TC_LOG_INFO("sql.updates", ">> %s database is up-to-date!", DBUpdater<T>::GetTableName().c_str()); + std::string const info = Trinity::StringFormat("Containing " SZFMTD " new and " SZFMTD " archived updates.", + result.recent, result.archived); + + if (!result.updated) + TC_LOG_INFO("sql.updates", ">> %s database is up-to-date! %s", DBUpdater<T>::GetTableName().c_str(), info.c_str()); else - TC_LOG_INFO("sql.updates", ">> Applied %d %s.", count, count == 1 ? "query" : "queries"); + TC_LOG_INFO("sql.updates", ">> Applied " SZFMTD " %s. %s", result.updated, result.updated == 1 ? "query" : "queries", info.c_str()); return true; } @@ -348,7 +351,9 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos // CLI Client connection info args.push_back("-h" + host); args.push_back("-u" + user); - args.push_back("-p" + password); + + if (!password.empty()) + args.push_back("-p" + password); // Check if we want to connect through ip or socket (Unix only) #ifdef _WIN32 diff --git a/src/server/shared/Updater/DBUpdater.h b/src/server/shared/Updater/DBUpdater.h index 0caf8a438fb..a2b12bed235 100644 --- a/src/server/shared/Updater/DBUpdater.h +++ b/src/server/shared/Updater/DBUpdater.h @@ -41,6 +41,19 @@ enum BaseLocation LOCATION_DOWNLOAD }; +struct UpdateResult +{ + UpdateResult() + : updated(0), recent(0), archived(0) { } + + UpdateResult(size_t const updated_, size_t const recent_, size_t const archived_) + : updated(updated_), recent(recent_), archived(archived_) { } + + size_t updated; + size_t recent; + size_t archived; +}; + template <class T> class DBUpdater { diff --git a/src/server/shared/Updater/UpdateFetcher.cpp b/src/server/shared/Updater/UpdateFetcher.cpp index a4bdd193743..ec023928b99 100644 --- a/src/server/shared/Updater/UpdateFetcher.cpp +++ b/src/server/shared/Updater/UpdateFetcher.cpp @@ -154,17 +154,27 @@ UpdateFetcher::SQLUpdate UpdateFetcher::ReadSQLUpdate(boost::filesystem::path co return update; } -uint32 UpdateFetcher::Update(bool const redundancyChecks, bool const allowRehash, bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const +UpdateResult UpdateFetcher::Update(bool const redundancyChecks, bool const allowRehash, bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const { LocaleFileStorage const available = GetFileList(); AppliedFileStorage applied = ReceiveAppliedFiles(); + size_t countRecentUpdates = 0; + size_t countArchivedUpdates = 0; + + // Count updates + for (auto const& entry : applied) + if (entry.second.state == RELEASED) + ++countRecentUpdates; + else + ++countArchivedUpdates; + // Fill hash to name cache HashToFileNameStorage hashToName; for (auto entry : applied) hashToName.insert(std::make_pair(entry.second.hash, entry.first)); - uint32 importedUpdates = 0; + size_t importedUpdates = 0; for (auto const& availableQuery : available) { @@ -314,7 +324,7 @@ uint32 UpdateFetcher::Update(bool const redundancyChecks, bool const allowRehash } } - return importedUpdates; + return UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates); } std::string UpdateFetcher::CalculateHash(SQLUpdate const& query) const @@ -329,7 +339,6 @@ std::string UpdateFetcher::CalculateHash(SQLUpdate const& query) const uint32 UpdateFetcher::Apply(Path const& path) const { using Time = std::chrono::high_resolution_clock; - using ms = std::chrono::milliseconds; // Benchmark query speed auto const begin = Time::now(); @@ -338,7 +347,7 @@ uint32 UpdateFetcher::Apply(Path const& path) const _applyFile(path); // Return time the query took to apply - return std::chrono::duration_cast<ms>(Time::now() - begin).count(); + return std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count(); } void UpdateFetcher::UpdateEntry(AppliedFileEntry const& entry, uint32 const speed) const diff --git a/src/server/shared/Updater/UpdateFetcher.h b/src/server/shared/Updater/UpdateFetcher.h index fa142547873..4ff8c93bc76 100644 --- a/src/server/shared/Updater/UpdateFetcher.h +++ b/src/server/shared/Updater/UpdateFetcher.h @@ -35,7 +35,7 @@ public: std::function<void(Path const& path)> const& applyFile, std::function<QueryResult(std::string const&)> const& retrieve); - uint32 Update(bool const redundancyChecks, bool const allowRehash, + UpdateResult Update(bool const redundancyChecks, bool const allowRehash, bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const; private: diff --git a/src/server/shared/Utilities/TaskScheduler.cpp b/src/server/shared/Utilities/TaskScheduler.cpp new file mode 100644 index 00000000000..801cc96cf77 --- /dev/null +++ b/src/server/shared/Utilities/TaskScheduler.cpp @@ -0,0 +1,229 @@ +/* + * 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 "TaskScheduler.h" + +TaskScheduler& TaskScheduler::ClearValidator() +{ + _predicate = EmptyValidator; + return *this; +} + +TaskScheduler& TaskScheduler::Update(success_t const& callback) +{ + _now = clock_t::now(); + Dispatch(callback); + return *this; +} + +TaskScheduler& TaskScheduler::Update(size_t const milliseconds, success_t const& callback) +{ + return Update(std::chrono::milliseconds(milliseconds), callback); +} + +TaskScheduler& TaskScheduler::Async(std::function<void()> const& callable) +{ + _asyncHolder.push(callable); + return *this; +} + +TaskScheduler& TaskScheduler::CancelAll() +{ + /// Clear the task holder + _task_holder.Clear(); + _asyncHolder = AsyncHolder(); + return *this; +} + +TaskScheduler& TaskScheduler::CancelGroup(group_t const group) +{ + _task_holder.RemoveIf([group](TaskContainer const& task) -> bool + { + return task->IsInGroup(group); + }); + return *this; +} + +TaskScheduler& TaskScheduler::CancelGroupsOf(std::vector<group_t> const& groups) +{ + std::for_each(groups.begin(), groups.end(), + std::bind(&TaskScheduler::CancelGroup, this, std::placeholders::_1)); + + return *this; +} + +TaskScheduler& TaskScheduler::InsertTask(TaskContainer task) +{ + _task_holder.Push(std::move(task)); + return *this; +} + +void TaskScheduler::Dispatch(success_t const& callback) +{ + // If the validation failed abort the dispatching here. + if (!_predicate()) + return; + + // Process all asyncs + while (!_asyncHolder.empty()) + { + _asyncHolder.front()(); + _asyncHolder.pop(); + + // If the validation failed abort the dispatching here. + if (!_predicate()) + return; + } + + while (!_task_holder.IsEmpty()) + { + if (_task_holder.First()->_end > _now) + break; + + // Perfect forward the context to the handler + // Use weak references to catch destruction before callbacks. + TaskContext context(_task_holder.Pop(), std::weak_ptr<TaskScheduler>(self_reference)); + + // Invoke the context + context.Invoke(); + + // If the validation failed abort the dispatching here. + if (!_predicate()) + return; + } + + // On finish call the final callback + callback(); +} + +void TaskScheduler::TaskQueue::Push(TaskContainer&& task) +{ + container.insert(task); +} + +auto TaskScheduler::TaskQueue::Pop() -> TaskContainer +{ + TaskContainer result = *container.begin(); + container.erase(container.begin()); + return result; +} + +auto TaskScheduler::TaskQueue::First() const -> TaskContainer const& +{ + return *container.begin(); +} + +void TaskScheduler::TaskQueue::Clear() +{ + container.clear(); +} + +void TaskScheduler::TaskQueue::RemoveIf(std::function<bool(TaskContainer const&)> const& filter) +{ + for (auto itr = container.begin(); itr != container.end();) + if (filter(*itr)) + itr = container.erase(itr); + else + ++itr; +} + +void TaskScheduler::TaskQueue::ModifyIf(std::function<bool(TaskContainer const&)> const& filter) +{ + std::vector<TaskContainer> cache; + for (auto itr = container.begin(); itr != container.end();) + if (filter(*itr)) + { + cache.push_back(*itr); + itr = container.erase(itr); + } + else + ++itr; + + container.insert(cache.begin(), cache.end()); +} + +bool TaskScheduler::TaskQueue::IsEmpty() const +{ + return container.empty(); +} + +TaskContext& TaskContext::Dispatch(std::function<TaskScheduler&(TaskScheduler&)> const& apply) +{ + if (auto const owner = _owner.lock()) + apply(*owner); + + return *this; +} + +bool TaskContext::IsExpired() const +{ + return _owner.expired(); +} + +bool TaskContext::IsInGroup(TaskScheduler::group_t const group) const +{ + return _task->IsInGroup(group); +} + +TaskContext& TaskContext::SetGroup(TaskScheduler::group_t const group) +{ + _task->_group = group; + return *this; +} + +TaskContext& TaskContext::ClearGroup() +{ + _task->_group = boost::none; + return *this; +} + +TaskScheduler::repeated_t TaskContext::GetRepeatCounter() const +{ + return _task->_repeated; +} + +TaskContext& TaskContext::Async(std::function<void()> const& callable) +{ + return Dispatch(std::bind(&TaskScheduler::Async, std::placeholders::_1, callable)); +} + +TaskContext& TaskContext::CancelAll() +{ + return Dispatch(std::mem_fn(&TaskScheduler::CancelAll)); +} + +TaskContext& TaskContext::CancelGroup(TaskScheduler::group_t const group) +{ + return Dispatch(std::bind(&TaskScheduler::CancelGroup, std::placeholders::_1, group)); +} + +TaskContext& TaskContext::CancelGroupsOf(std::vector<TaskScheduler::group_t> const& groups) +{ + return Dispatch(std::bind(&TaskScheduler::CancelGroupsOf, std::placeholders::_1, std::cref(groups))); +} + +void TaskContext::AssertOnConsumed() const +{ + // This was adapted to TC to prevent static analysis tools from complaining. + // If you encounter this assertion check if you repeat a TaskContext more then 1 time! + ASSERT(!(*_consumed) && "Bad task logic, task context was consumed already!"); +} + +void TaskContext::Invoke() +{ + _task->_task(*this); +} diff --git a/src/server/shared/Utilities/TaskScheduler.h b/src/server/shared/Utilities/TaskScheduler.h new file mode 100644 index 00000000000..d45835b5f17 --- /dev/null +++ b/src/server/shared/Utilities/TaskScheduler.h @@ -0,0 +1,661 @@ +/* + * 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/>. + */ + +#ifndef _TASK_SCHEDULER_H_ +#define _TASK_SCHEDULER_H_ + +#include <algorithm> +#include <chrono> +#include <vector> +#include <queue> +#include <memory> +#include <utility> +#include <set> + +#include <boost/optional.hpp> + +#include "Util.h" + +class TaskContext; + +/// The TaskScheduler class provides the ability to schedule std::function's in the near future. +/// Use TaskScheduler::Update to update the scheduler. +/// Popular methods are: +/// * Schedule (Schedules a std::function which will be executed in the near future). +/// * Schedules an asynchronous function which will be executed at the next update tick. +/// * Cancel, Delay & Reschedule (Methods to manipulate already scheduled tasks). +/// Tasks are organized in groups (uint), multiple tasks can have the same group id, +/// you can provide a group or not, but keep in mind that you can only manipulate specific tasks through its group id! +/// Tasks callbacks use the function signature void(TaskContext) where TaskContext provides +/// access to the function schedule plan which makes it possible to repeat the task +/// with the same duration or a new one. +/// It also provides access to the repeat counter which is useful for task that repeat itself often +/// but behave different every time (spoken event dialogs for example). +class TaskScheduler +{ + friend class TaskContext; + + // Time definitions (use steady clock) + typedef std::chrono::steady_clock clock_t; + typedef clock_t::time_point timepoint_t; + typedef clock_t::duration duration_t; + + // Task group type + typedef uint32 group_t; + // Task repeated type + typedef uint32 repeated_t; + // Task handle type + typedef std::function<void(TaskContext)> task_handler_t; + // Predicate type + typedef std::function<bool()> predicate_t; + // Success handle type + typedef std::function<void()> success_t; + + class Task + { + friend class TaskContext; + friend class TaskScheduler; + + timepoint_t _end; + duration_t _duration; + boost::optional<group_t> _group; + repeated_t _repeated; + task_handler_t _task; + + public: + // All Argument construct + Task(timepoint_t const& end, duration_t const& duration, boost::optional<group_t> const& group, + repeated_t const repeated, task_handler_t const& task) + : _end(end), _duration(duration), _group(group), _repeated(repeated), _task(task) { } + + // Minimal Argument construct + Task(timepoint_t const& end, duration_t const& duration, task_handler_t const& task) + : _end(end), _duration(duration), _group(boost::none), _repeated(0), _task(task) { } + + // Copy construct + Task(Task const&) = delete; + // Move construct + Task(Task&&) = delete; + // Copy Assign + Task& operator= (Task const&) = default; + // Move Assign + Task& operator= (Task&& right) = delete; + + // Order tasks by its end + inline bool operator< (Task const& other) const + { + return _end < other._end; + } + + inline bool operator> (Task const& other) const + { + return _end > other._end; + } + + // Compare tasks with its end + inline bool operator== (Task const& other) + { + return _end == other._end; + } + + // Returns true if the task is in the given group + inline bool IsInGroup(group_t const group) const + { + return _group == group; + } + }; + + typedef std::shared_ptr<Task> TaskContainer; + + /// Container which provides Task order, insert and reschedule operations. + struct Compare + { + bool operator() (TaskContainer const& left, TaskContainer const& right) + { + return (*left.get()) < (*right.get()); + }; + }; + + class TaskQueue + { + std::multiset<TaskContainer, Compare> container; + + public: + // Pushes the task in the container + void Push(TaskContainer&& task); + + /// Pops the task out of the container + TaskContainer Pop(); + + TaskContainer const& First() const; + + void Clear(); + + void RemoveIf(std::function<bool(TaskContainer const&)> const& filter); + + void ModifyIf(std::function<bool(TaskContainer const&)> const& filter); + + bool IsEmpty() const; + }; + + /// Contains a self reference to track if this object was deleted or not. + std::shared_ptr<TaskScheduler> self_reference; + + /// The current time point (now) + timepoint_t _now; + + /// The Task Queue which contains all task objects. + TaskQueue _task_holder; + + typedef std::queue<std::function<void()>> AsyncHolder; + + /// Contains all asynchronous tasks which will be invoked at + /// the next update tick. + AsyncHolder _asyncHolder; + + predicate_t _predicate; + + static bool EmptyValidator() + { + return true; + } + + static void EmptyCallback() + { + } + +public: + TaskScheduler() + : self_reference(this, [](TaskScheduler const*) { }), _now(clock_t::now()), _predicate(EmptyValidator) { } + + template<typename P> + TaskScheduler(P&& predicate) + : self_reference(this, [](TaskScheduler const*) { }), _now(clock_t::now()), _predicate(std::forward<P>(predicate)) { } + + TaskScheduler(TaskScheduler const&) = delete; + TaskScheduler(TaskScheduler&&) = delete; + TaskScheduler& operator= (TaskScheduler const&) = delete; + TaskScheduler& operator= (TaskScheduler&&) = delete; + + /// Sets a validator which is asked if tasks are allowed to be executed. + template<typename P> + TaskScheduler& SetValidator(P&& predicate) + { + _predicate = std::forward<P>(predicate); + return *this; + } + + /// Clears the validator which is asked if tasks are allowed to be executed. + TaskScheduler& ClearValidator(); + + /// Update the scheduler to the current time. + /// Calls the optional callback on successfully finish. + TaskScheduler& Update(success_t const& callback = EmptyCallback); + + /// Update the scheduler with a difftime in ms. + /// Calls the optional callback on successfully finish. + TaskScheduler& Update(size_t const milliseconds, success_t const& callback = EmptyCallback); + + /// Update the scheduler with a difftime. + /// Calls the optional callback on successfully finish. + template<class _Rep, class _Period> + TaskScheduler& Update(std::chrono::duration<_Rep, _Period> const& difftime, + success_t const& callback = EmptyCallback) + { + _now += difftime; + Dispatch(callback); + return *this; + } + + /// Schedule an callable function that is executed at the next update tick. + /// Its safe to modify the TaskScheduler from within the callable. + TaskScheduler& Async(std::function<void()> const& callable); + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template<class _Rep, class _Period> + TaskScheduler& Schedule(std::chrono::duration<_Rep, _Period> const& time, + task_handler_t const& task) + { + return ScheduleAt(_now, time, task); + } + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template<class _Rep, class _Period> + TaskScheduler& Schedule(std::chrono::duration<_Rep, _Period> const& time, + group_t const group, task_handler_t const& task) + { + return ScheduleAt(_now, time, group, task); + } + + /// Schedule an event with a randomized rate between min and max rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, task_handler_t const& task) + { + return Schedule(RandomDurationBetween(min, max), task); + } + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, group_t const group, + task_handler_t const& task) + { + return Schedule(RandomDurationBetween(min, max), group, task); + } + + /// Cancels all tasks. + /// Never call this from within a task context! Use TaskContext::CancelAll instead! + TaskScheduler& CancelAll(); + + /// Cancel all tasks of a single group. + /// Never call this from within a task context! Use TaskContext::CancelGroup instead! + TaskScheduler& CancelGroup(group_t const group); + + /// Cancels all groups in the given std::vector. + /// Hint: Use std::initializer_list for this: "{1, 2, 3, 4}" + TaskScheduler& CancelGroupsOf(std::vector<group_t> const& groups); + + /// Delays all tasks with the given duration. + template<class _Rep, class _Period> + TaskScheduler& DelayAll(std::chrono::duration<_Rep, _Period> const& duration) + { + _task_holder.ModifyIf([&duration](TaskContainer const& task) -> bool + { + task->_end += duration; + return true; + }); + return *this; + } + + /// Delays all tasks with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& DelayAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayAll(RandomDurationBetween(min, max)); + } + + /// Delays all tasks of a group with the given duration. + template<class _Rep, class _Period> + TaskScheduler& DelayGroup(group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + _task_holder.ModifyIf([&duration, group](TaskContainer const& task) -> bool + { + if (task->IsInGroup(group)) + { + task->_end += duration; + return true; + } + else + return false; + }); + return *this; + } + + /// Delays all tasks of a group with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& DelayGroup(group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayGroup(group, RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks with a given duration. + template<class _Rep, class _Period> + TaskScheduler& RescheduleAll(std::chrono::duration<_Rep, _Period> const& duration) + { + auto const end = _now + duration; + _task_holder.ModifyIf([end](TaskContainer const& task) -> bool + { + task->_end = end; + return true; + }); + return *this; + } + + /// Reschedule all tasks with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& RescheduleAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleAll(RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks of a group with the given duration. + template<class _Rep, class _Period> + TaskScheduler& RescheduleGroup(group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + auto const end = _now + duration; + _task_holder.ModifyIf([end, group](TaskContainer const& task) -> bool + { + if (task->IsInGroup(group)) + { + task->_end = end; + return true; + } + else + return false; + }); + return *this; + } + + /// Reschedule all tasks of a group with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& RescheduleGroup(group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleGroup(group, RandomDurationBetween(min, max)); + } + +private: + /// Insert a new task to the enqueued tasks. + TaskScheduler& InsertTask(TaskContainer task); + + template<class _Rep, class _Period> + TaskScheduler& ScheduleAt(timepoint_t const& end, + std::chrono::duration<_Rep, _Period> const& time, task_handler_t const& task) + { + return InsertTask(TaskContainer(new Task(end + time, time, task))); + } + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::schedule instead! + template<class _Rep, class _Period> + TaskScheduler& ScheduleAt(timepoint_t const& end, + std::chrono::duration<_Rep, _Period> const& time, + group_t const group, task_handler_t const& task) + { + static repeated_t const DEFAULT_REPEATED = 0; + return InsertTask(TaskContainer(new Task(end + time, time, group, DEFAULT_REPEATED, task))); + } + + // Returns a random duration between min and max + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + static std::chrono::milliseconds + RandomDurationBetween(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + auto const milli_min = std::chrono::duration_cast<std::chrono::milliseconds>(min); + auto const milli_max = std::chrono::duration_cast<std::chrono::milliseconds>(max); + + // TC specific: use SFMT URandom + return std::chrono::milliseconds(urand(milli_min.count(), milli_max.count())); + } + + /// Dispatch remaining tasks + void Dispatch(success_t const& callback); +}; + +class TaskContext +{ + friend class TaskScheduler; + + /// Associated task + TaskScheduler::TaskContainer _task; + + /// Owner + std::weak_ptr<TaskScheduler> _owner; + + /// Marks the task as consumed + std::shared_ptr<bool> _consumed; + + /// Dispatches an action safe on the TaskScheduler + TaskContext& Dispatch(std::function<TaskScheduler&(TaskScheduler&)> const& apply); + +public: + // Empty constructor + TaskContext() + : _task(), _owner(), _consumed(std::make_shared<bool>(true)) { } + + // Construct from task and owner + explicit TaskContext(TaskScheduler::TaskContainer&& task, std::weak_ptr<TaskScheduler>&& owner) + : _task(task), _owner(owner), _consumed(std::make_shared<bool>(false)) { } + + // Copy construct + TaskContext(TaskContext const& right) + : _task(right._task), _owner(right._owner), _consumed(right._consumed) { } + + // Move construct + TaskContext(TaskContext&& right) + : _task(std::move(right._task)), _owner(std::move(right._owner)), _consumed(std::move(right._consumed)) { } + + // Copy assign + TaskContext& operator= (TaskContext const& right) + { + _task = right._task; + _owner = right._owner; + _consumed = right._consumed; + return *this; + } + + // Move assign + TaskContext& operator= (TaskContext&& right) + { + _task = std::move(right._task); + _owner = std::move(right._owner); + _consumed = std::move(right._consumed); + return *this; + } + + /// Returns true if the owner was deallocated and this context has expired. + bool IsExpired() const; + + /// Returns true if the event is in the given group + bool IsInGroup(TaskScheduler::group_t const group) const; + + /// Sets the event in the given group + TaskContext& SetGroup(TaskScheduler::group_t const group); + + /// Removes the group from the event + TaskContext& ClearGroup(); + + /// Returns the repeat counter which increases every time the task is repeated. + TaskScheduler::repeated_t GetRepeatCounter() const; + + /// Repeats the event and sets a new duration. + /// std::chrono::seconds(5) for example. + /// This will consume the task context, its not possible to repeat the task again + /// from the same task context! + template<class _Rep, class _Period> + TaskContext& Repeat(std::chrono::duration<_Rep, _Period> const& duration) + { + AssertOnConsumed(); + + // Set new duration, in-context timing and increment repeat counter + _task->_duration = duration; + _task->_end += duration; + _task->_repeated += 1; + (*_consumed) = true; + return Dispatch(std::bind(&TaskScheduler::InsertTask, std::placeholders::_1, _task)); + } + + /// Repeats the event with the same duration. + /// This will consume the task context, its not possible to repeat the task again + /// from the same task context! + TaskContext& Repeat() + { + return Repeat(_task->_duration); + } + + /// Repeats the event and set a new duration that is randomized between min and max. + /// std::chrono::seconds(5) for example. + /// This will consume the task context, its not possible to repeat the task again + /// from the same task context! + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContext& Repeat(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return Repeat(TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Schedule a callable function that is executed at the next update tick from within the context. + /// Its safe to modify the TaskScheduler from within the callable. + TaskContext& Async(std::function<void()> const& callable); + + /// Schedule an event with a fixed rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template<class _Rep, class _Period> + TaskContext& Schedule(std::chrono::duration<_Rep, _Period> const& time, + TaskScheduler::task_handler_t const& task) + { + auto const end = _task->_end; + return Dispatch([end, time, task](TaskScheduler& scheduler) -> TaskScheduler& + { + return scheduler.ScheduleAt<_Rep, _Period>(end, time, task); + }); + } + + /// Schedule an event with a fixed rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template<class _Rep, class _Period> + TaskContext& Schedule(std::chrono::duration<_Rep, _Period> const& time, + TaskScheduler::group_t const group, TaskScheduler::task_handler_t const& task) + { + auto const end = _task->_end; + return Dispatch([end, time, group, task](TaskScheduler& scheduler) -> TaskScheduler& + { + return scheduler.ScheduleAt<_Rep, _Period>(end, time, group, task); + }); + } + + /// Schedule an event with a randomized rate between min and max rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContext& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, TaskScheduler::task_handler_t const& task) + { + return Schedule(TaskScheduler::RandomDurationBetween(min, max), task); + } + + /// Schedule an event with a randomized rate between min and max rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContext& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, TaskScheduler::group_t const group, + TaskScheduler::task_handler_t const& task) + { + return Schedule(TaskScheduler::RandomDurationBetween(min, max), group, task); + } + + /// Cancels all tasks from within the context. + TaskContext& CancelAll(); + + /// Cancel all tasks of a single group from within the context. + TaskContext& CancelGroup(TaskScheduler::group_t const group); + + /// Cancels all groups in the given std::vector from within the context. + /// Hint: Use std::initializer_list for this: "{1, 2, 3, 4}" + TaskContext& CancelGroupsOf(std::vector<TaskScheduler::group_t> const& groups); + + /// Delays all tasks with the given duration from within the context. + template<class _Rep, class _Period> + TaskContext& DelayAll(std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::DelayAll<_Rep, _Period>, std::placeholders::_1, duration)); + } + + /// Delays all tasks with a random duration between min and max from within the context. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContext& DelayAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayAll(TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Delays all tasks of a group with the given duration from within the context. + template<class _Rep, class _Period> + TaskContext& DelayGroup(TaskScheduler::group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::DelayGroup<_Rep, _Period>, std::placeholders::_1, group, duration)); + } + + /// Delays all tasks of a group with a random duration between min and max from within the context. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContext& DelayGroup(TaskScheduler::group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayGroup(group, TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks with the given duration. + template<class _Rep, class _Period> + TaskContext& RescheduleAll(std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::RescheduleAll, std::placeholders::_1, duration)); + } + + /// Reschedule all tasks with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContext& RescheduleAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleAll(TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks of a group with the given duration. + template<class _Rep, class _Period> + TaskContext& RescheduleGroup(TaskScheduler::group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::RescheduleGroup<_Rep, _Period>, std::placeholders::_1, group, duration)); + } + + /// Reschedule all tasks of a group with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContext& RescheduleGroup(TaskScheduler::group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleGroup(group, TaskScheduler::RandomDurationBetween(min, max)); + } + +private: + /// Asserts if the task was consumed already. + void AssertOnConsumed() const; + + /// Invokes the associated hook of the task. + void Invoke(); +}; + +/// Milliseconds shorthand typedef. +typedef std::chrono::milliseconds Milliseconds; + +/// Seconds shorthand typedef. +typedef std::chrono::seconds Seconds; + +/// Minutes shorthand typedef. +typedef std::chrono::minutes Minutes; + +/// Hours shorthand typedef. +typedef std::chrono::hours Hours; + +#endif /// _TASK_SCHEDULER_H_ diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 2758c8e39cd..447ef9a45a8 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1021,7 +1021,7 @@ OffhandCheckAtSpellUnlearn = 1 # ClientCacheVersion # Description: Client cache version for client cache data reset. Use any value different # from DB and not recently been used to trigger client side cache reset. -# Default: 0 - (Use DB value from world DB db_version.cache_id field) +# Default: 0 - (Use DB value from world DB version.cache_id field) ClientCacheVersion = 0 diff --git a/src/tools/map_extractor/adt.h b/src/tools/map_extractor/adt.h index 9212784ab9e..cad773fbee4 100644 --- a/src/tools/map_extractor/adt.h +++ b/src/tools/map_extractor/adt.h @@ -67,8 +67,8 @@ class adt_MCLQ uint32 fcc; char fcc_txt[4]; }; - uint32 size; public: + uint32 size; float height1; float height2; struct liquid_data{ @@ -96,8 +96,8 @@ class adt_MCNK uint32 fcc; char fcc_txt[4]; }; - uint32 size; public: + uint32 size; uint32 flags; uint32 ix; uint32 iy; @@ -155,8 +155,8 @@ class adt_MCIN uint32 fcc; char fcc_txt[4]; }; - uint32 size; public: + uint32 size; struct adt_CELLS{ uint32 offsMCNK; uint32 size; @@ -271,6 +271,7 @@ class adt_MHDR uint32 fcc; char fcc_txt[4]; }; +public: uint32 size; uint32 pad; @@ -289,7 +290,6 @@ class adt_MHDR uint32 data3; uint32 data4; uint32 data5; -public: bool prepareLoadedData(); adt_MCIN *getMCIN(){ return (adt_MCIN *)((uint8 *)&pad+offsMCIN);} adt_MH2O *getMH2O(){ return offsMH2O ? (adt_MH2O *)((uint8 *)&pad+offsMH2O) : 0;} diff --git a/src/tools/map_extractor/mpq_libmpq.cpp b/src/tools/map_extractor/mpq_libmpq.cpp index 3e12747e9c5..482e3a3abbd 100644 --- a/src/tools/map_extractor/mpq_libmpq.cpp +++ b/src/tools/map_extractor/mpq_libmpq.cpp @@ -71,7 +71,7 @@ MPQFile::MPQFile(const char* filename): uint32_t filenum; if(libmpq__file_number(mpq_a, filename, &filenum)) continue; libmpq__off_t transferred; - libmpq__file_unpacked_size(mpq_a, filenum, &size); + libmpq__file_size_unpacked(mpq_a, filenum, &size); // HACK: in patch.mpq some files don't want to open and give 1 for filesize if (size<=1) { diff --git a/src/tools/map_extractor/mpq_libmpq04.h b/src/tools/map_extractor/mpq_libmpq04.h index 470e7bd0c50..c6fe36a8221 100644 --- a/src/tools/map_extractor/mpq_libmpq04.h +++ b/src/tools/map_extractor/mpq_libmpq04.h @@ -43,7 +43,7 @@ public: uint32_t filenum; if(libmpq__file_number(mpq_a, "(listfile)", &filenum)) return; libmpq__off_t size, transferred; - libmpq__file_unpacked_size(mpq_a, filenum, &size); + libmpq__file_size_unpacked(mpq_a, filenum, &size); char *buffer = new char[size+1]; buffer[size] = '\0'; diff --git a/src/tools/vmap4_extractor/mpq_libmpq.cpp b/src/tools/vmap4_extractor/mpq_libmpq.cpp index 5e0effc1a77..f3eb3da96b6 100644 --- a/src/tools/vmap4_extractor/mpq_libmpq.cpp +++ b/src/tools/vmap4_extractor/mpq_libmpq.cpp @@ -71,7 +71,7 @@ MPQFile::MPQFile(const char* filename): uint32 filenum; if(libmpq__file_number(mpq_a, filename, &filenum)) continue; libmpq__off_t transferred; - libmpq__file_unpacked_size(mpq_a, filenum, &size); + libmpq__file_size_unpacked(mpq_a, filenum, &size); // HACK: in patch.mpq some files don't want to open and give 1 for filesize if (size<=1) { diff --git a/src/tools/vmap4_extractor/mpq_libmpq04.h b/src/tools/vmap4_extractor/mpq_libmpq04.h index bb842daf258..6196285627d 100644 --- a/src/tools/vmap4_extractor/mpq_libmpq04.h +++ b/src/tools/vmap4_extractor/mpq_libmpq04.h @@ -42,7 +42,7 @@ public: uint32_t filenum; if(libmpq__file_number(mpq_a, "(listfile)", &filenum)) return; libmpq__off_t size, transferred; - libmpq__file_unpacked_size(mpq_a, filenum, &size); + libmpq__file_size_unpacked(mpq_a, filenum, &size); char *buffer = new char[size + 1]; buffer[size] = '\0'; |