diff options
-rw-r--r-- | sql/updates/world/master/2022_06_25_01_world.sql | 19 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 3 | ||||
-rw-r--r-- | src/server/game/Maps/Map.cpp | 28 | ||||
-rw-r--r-- | src/server/game/Maps/Map.h | 12 | ||||
-rw-r--r-- | src/server/game/Scripting/ScriptMgr.cpp | 23 | ||||
-rw-r--r-- | src/server/game/Scripting/ScriptMgr.h | 19 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 4 | ||||
-rw-r--r-- | src/server/game/World/WorldStates/WorldStateDefines.h | 36 | ||||
-rw-r--r-- | src/server/game/World/WorldStates/WorldStateMgr.cpp | 137 | ||||
-rw-r--r-- | src/server/game/World/WorldStates/WorldStateMgr.h | 50 |
10 files changed, 331 insertions, 0 deletions
diff --git a/sql/updates/world/master/2022_06_25_01_world.sql b/sql/updates/world/master/2022_06_25_01_world.sql new file mode 100644 index 00000000000..72693acf060 --- /dev/null +++ b/sql/updates/world/master/2022_06_25_01_world.sql @@ -0,0 +1,19 @@ +-- +-- Table structure for table `world_state` +-- +DROP TABLE IF EXISTS `world_state`; +CREATE TABLE `world_state` ( + `ID` int NOT NULL, + `DefaultValue` int NOT NULL, + `MapID` int unsigned DEFAULT NULL, + `ScriptName` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `Comment` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + PRIMARY KEY (`ID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +INSERT INTO `world_state` (`ID`, `DefaultValue`, `MapID`, `Comment`) VALUES +(5644, 0, 669, 'Blackwing Descent - Omnotron Defense System - Achieve-a-tron'), +(5645, 0, 669, 'Blackwing Descent - Omnotron Defense System - Achieve-a-tron'), +(5646, 0, 669, 'Blackwing Descent - Omnotron Defense System - Achieve-a-tron'), +(5647, 0, 669, 'Blackwing Descent - Omnotron Defense System - Achieve-a-tron'), +(5117, 0, 670, 'Grim Batol - Erudax - Don\'t need to Break Eggs to Make an Omelet'); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 2e46042d19a..5394a667d6c 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -124,6 +124,7 @@ #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "WorldStateMgr.h" #include "WorldStatePackets.h" #include <G3D/g3dmath.h> #include <sstream> @@ -9214,6 +9215,8 @@ void Player::SendInitWorldStates(uint32 zoneId, uint32 areaId) packet.AreaID = zoneId; packet.SubareaID = areaId; + sWorldStateMgr->FillInitialWorldStates(packet, GetMap()); + packet.Worldstates.emplace_back(2264, 0); // SCOURGE_EVENT_WORLDSTATE_EASTERN_PLAGUELANDS packet.Worldstates.emplace_back(2263, 0); // SCOURGE_EVENT_WORLDSTATE_TANARIS packet.Worldstates.emplace_back(2262, 0); // SCOURGE_EVENT_WORLDSTATE_BURNING_STEPPES diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index e9fe8caffc3..7771fe6198e 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -56,6 +56,8 @@ #include "WeatherMgr.h" #include "World.h" #include "WorldSession.h" +#include "WorldStateMgr.h" +#include "WorldStatePackets.h" #include <sstream> #include "Hacks/boost_1_74_fibonacci_heap.h" @@ -371,6 +373,8 @@ i_scriptLock(false), _respawnCheckTimer(0) MMAP::MMapFactory::createOrGetMMapManager()->loadMapInstance(sWorld->GetDataPath(), GetId(), i_InstanceId); + _worldStateValues = sWorldStateMgr->GetInitialWorldStatesForMap(this); + sScriptMgr->OnCreateMap(this); } @@ -684,6 +688,30 @@ void Map::UpdatePersonalPhasesForPlayer(Player const* player) GetMultiPersonalPhaseTracker().OnOwnerPhaseChanged(player, getNGrid(cell.GridX(), cell.GridY()), this, cell); } +int32 Map::GetWorldStateValue(int32 worldStateId) const +{ + if (int32 const* value = Trinity::Containers::MapGetValuePtr(_worldStateValues, worldStateId)) + return *value; + + return 0; +} + +void Map::SetWorldStateValue(int32 worldStateId, int32 value) +{ + auto itr = _worldStateValues.try_emplace(worldStateId, 0).first; + int32 oldValue = itr->second; + itr->second = value; + + if (WorldStateTemplate const* worldStateTemplate = sWorldStateMgr->GetWorldStateTemplate(worldStateId)) + sScriptMgr->OnWorldStateValueChange(worldStateTemplate, oldValue, value, this); + + // Broadcast update to all players on the map + WorldPackets::WorldState::UpdateWorldState updateWorldState; + updateWorldState.VariableID = worldStateId; + updateWorldState.Value = value; + SendToPlayers(updateWorldState.Write()); +} + template<class T> void Map::InitializeObject(T* /*obj*/) { } diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index f589ce9e0e3..1d795edfdd5 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -35,6 +35,7 @@ #include "SharedDefines.h" #include "SpawnData.h" #include "Timer.h" +#include "WorldStateDefines.h" #include <boost/heap/fibonacci_heap.hpp> #include <bitset> #include <list> @@ -918,6 +919,17 @@ class TC_GAME_API Map : public GridRefManager<NGridType> private: MultiPersonalPhaseTracker _multiPersonalPhaseTracker; + + /*********************************************************/ + /*** WorldStates ***/ + /*********************************************************/ + public: + int32 GetWorldStateValue(int32 worldStateId) const; + void SetWorldStateValue(int32 worldStateId, int32 value); + WorldStateValueContainer const& GetWorldStateValues() const { return _worldStateValues; } + + private: + WorldStateValueContainer _worldStateValues; }; enum InstanceResetMethod diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 795b8e997eb..c911afdad87 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -133,6 +133,10 @@ template<> struct is_script_database_bound<QuestScript> : std::true_type { }; +template<> +struct is_script_database_bound<WorldStateScript> + : std::true_type { }; + enum Spells { SPELL_HOTSWAP_VISUAL_SPELL_EFFECT = 40162 // 59084 @@ -2322,6 +2326,7 @@ void ScriptMgr::OnSceneComplete(Player* player, uint32 sceneInstanceID, SceneTem tmpscript->OnSceneComplete(player, sceneInstanceID, sceneTemplate); } +// Quest void ScriptMgr::OnQuestStatusChange(Player* player, Quest const* quest, QuestStatus oldStatus, QuestStatus newStatus) { ASSERT(player); @@ -2349,6 +2354,15 @@ void ScriptMgr::OnQuestObjectiveChange(Player* player, Quest const* quest, Quest tmpscript->OnQuestObjectiveChange(player, quest, objective, oldAmount, newAmount); } +// WorldState +void ScriptMgr::OnWorldStateValueChange(WorldStateTemplate const* worldStateTemplate, int32 oldValue, int32 newValue, Map const* map) +{ + ASSERT(worldStateTemplate); + + GET_SCRIPT(WorldStateScript, worldStateTemplate->ScriptId, tmpscript); + tmpscript->OnValueChange(worldStateTemplate->Id, oldValue, newValue, map); +} + SpellScriptLoader::SpellScriptLoader(char const* name) : ScriptObject(name) { @@ -2638,6 +2652,14 @@ QuestScript::QuestScript(char const* name) QuestScript::~QuestScript() = default; +WorldStateScript::WorldStateScript(char const* name) + : ScriptObject(name) +{ + ScriptRegistry<WorldStateScript>::Instance()->AddScript(this); +} + +WorldStateScript::~WorldStateScript() = default; + // Specialize for each script type class like so: template class TC_GAME_API ScriptRegistry<SpellScriptLoader>; template class TC_GAME_API ScriptRegistry<ServerScript>; @@ -2671,3 +2693,4 @@ template class TC_GAME_API ScriptRegistry<AreaTriggerEntityScript>; template class TC_GAME_API ScriptRegistry<ConversationScript>; template class TC_GAME_API ScriptRegistry<SceneScript>; template class TC_GAME_API ScriptRegistry<QuestScript>; +template class TC_GAME_API ScriptRegistry<WorldStateScript>; diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 32ecfb431c7..e5fd8bee5b9 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -77,6 +77,7 @@ struct MapEntry; struct Position; struct QuestObjective; struct SceneTemplate; +struct WorldStateTemplate; namespace Trinity::ChatCommands { struct ChatCommandBuilder; } @@ -987,6 +988,20 @@ class TC_GAME_API QuestScript : public ScriptObject virtual void OnQuestObjectiveChange(Player* /*player*/, Quest const* /*quest*/, QuestObjective const& /*objective*/, int32 /*oldAmount*/, int32 /*newAmount*/) { } }; +class TC_GAME_API WorldStateScript : public ScriptObject +{ + protected: + + WorldStateScript(char const* name); + + public: + + ~WorldStateScript(); + + // Called when worldstate changes value, map is optional + virtual void OnValueChange([[maybe_unused]] int32 worldStateId, [[maybe_unused]] int32 oldValue, [[maybe_unused]] int32 newValue, [[maybe_unused]] Map const* map) { } +}; + // Manages registration, loading, and execution of scripts. class TC_GAME_API ScriptMgr { @@ -1289,6 +1304,10 @@ class TC_GAME_API ScriptMgr void OnQuestAcknowledgeAutoAccept(Player* player, Quest const* quest); void OnQuestObjectiveChange(Player* player, Quest const* quest, QuestObjective const& objective, int32 oldAmount, int32 newAmount); + public: /* WorldStateScript */ + + void OnWorldStateValueChange(WorldStateTemplate const* worldStateTemplate, int32 oldValue, int32 newValue, Map const* map); + private: uint32 _scriptCount; bool _scriptIdUpdated; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 5a3ef8104f3..94b36940189 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -100,6 +100,7 @@ #include "WhoListStorage.h" #include "WorldSession.h" #include "WorldSocket.h" +#include "WorldStateMgr.h" #include <boost/algorithm/string.hpp> @@ -2241,6 +2242,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Creature Formations..."); sFormationMgr->LoadCreatureFormations(); + TC_LOG_INFO("server.loading", "Loading World State templates..."); + sWorldStateMgr->LoadFromDB(); + TC_LOG_INFO("server.loading", "Loading World States..."); // must be loaded before battleground, outdoor PvP and conditions LoadWorldStates(); diff --git a/src/server/game/World/WorldStates/WorldStateDefines.h b/src/server/game/World/WorldStates/WorldStateDefines.h new file mode 100644 index 00000000000..3b85a36d1e4 --- /dev/null +++ b/src/server/game/World/WorldStates/WorldStateDefines.h @@ -0,0 +1,36 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 WorldStateDefines_h__ +#define WorldStateDefines_h__ + +#include "Define.h" +#include "Optional.h" +#include <unordered_map> + +struct WorldStateTemplate +{ + int32 Id = 0; + int32 DefaultValue = 0; + uint32 ScriptId = 0; + + Optional<uint32> MapId; +}; + +using WorldStateValueContainer = std::unordered_map<int32 /*worldStateId*/, int32 /*value*/>; + +#endif // WorldStateDefines_h__ diff --git a/src/server/game/World/WorldStates/WorldStateMgr.cpp b/src/server/game/World/WorldStates/WorldStateMgr.cpp new file mode 100644 index 00000000000..b9ed79f951a --- /dev/null +++ b/src/server/game/World/WorldStates/WorldStateMgr.cpp @@ -0,0 +1,137 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 "WorldStateMgr.h" +#include "Containers.h" +#include "DatabaseEnv.h" +#include "Log.h" +#include "Map.h" +#include "ObjectMgr.h" +#include "ScriptMgr.h" +#include "World.h" +#include "WorldStatePackets.h" + +namespace +{ +std::unordered_map<int32, WorldStateTemplate> _worldStateTemplates; +WorldStateValueContainer _realmWorldStateValues; +std::unordered_map<uint32, WorldStateValueContainer> _worldStatesByMap; +} + +void WorldStateMgr::LoadFromDB() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT ID, DefaultValue, MapID, ScriptName FROM world_state"); + if (!result) + return; + + do + { + Field* fields = result->Fetch(); + + int32 id = fields[0].GetInt32(); + WorldStateTemplate& worldState = _worldStateTemplates[id]; + worldState.Id = id; + worldState.DefaultValue = fields[1].GetInt32(); + if (!fields[2].IsNull()) + worldState.MapId = fields[2].GetUInt32(); + + worldState.ScriptId = sObjectMgr->GetScriptId(fields[3].GetString()); + + if (worldState.MapId) + _worldStatesByMap[*worldState.MapId][id] = worldState.DefaultValue; + else + _realmWorldStateValues[id] = worldState.DefaultValue; + + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " world state templates %u ms", _worldStateTemplates.size(), GetMSTimeDiffToNow(oldMSTime)); +} + +WorldStateTemplate const* WorldStateMgr::GetWorldStateTemplate(int32 worldStateId) const +{ + return Trinity::Containers::MapGetValuePtr(_worldStateTemplates, worldStateId); +} + +int32 WorldStateMgr::GetValue(int32 worldStateId, Map const* map) const +{ + WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId); + if (!worldStateTemplate || !worldStateTemplate->MapId) + { + if (int32 const* value = Trinity::Containers::MapGetValuePtr(_realmWorldStateValues, worldStateId)) + return *value; + + return 0; + } + + if (map->GetId() != worldStateTemplate->MapId) + return 0; + + return map->GetWorldStateValue(worldStateId); +} + +void WorldStateMgr::SetValue(int32 worldStateId, int32 value, Map* map) +{ + WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId); + if (!worldStateTemplate || !worldStateTemplate->MapId) + { + auto itr = _realmWorldStateValues.try_emplace(worldStateId, 0).first; + int32 oldValue = itr->second; + itr->second = value; + + if (worldStateTemplate) + sScriptMgr->OnWorldStateValueChange(worldStateTemplate, oldValue, value, nullptr); + + // Broadcast update to all players on the server + WorldPackets::WorldState::UpdateWorldState updateWorldState; + updateWorldState.VariableID = worldStateId; + updateWorldState.Value = value; + sWorld->SendGlobalMessage(updateWorldState.Write()); + return; + } + + if (map->GetId() != worldStateTemplate->MapId) + return; + + map->SetWorldStateValue(worldStateId, value); +} + +WorldStateValueContainer WorldStateMgr::GetInitialWorldStatesForMap(Map const* map) const +{ + WorldStateValueContainer initialValues; + if (WorldStateValueContainer const* valuesTemplate = Trinity::Containers::MapGetValuePtr(_worldStatesByMap, map->GetId())) + initialValues = *valuesTemplate; + + return initialValues; +} + +void WorldStateMgr::FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& initWorldStates, Map const* map) const +{ + for (auto const& [worldStateId, value] : _realmWorldStateValues) + initWorldStates.Worldstates.emplace_back(worldStateId, value); + + for (auto const& [worldStateId, value] : map->GetWorldStateValues()) + initWorldStates.Worldstates.emplace_back(worldStateId, value); +} + +WorldStateMgr* WorldStateMgr::instance() +{ + static WorldStateMgr instance; + return &instance; +} diff --git a/src/server/game/World/WorldStates/WorldStateMgr.h b/src/server/game/World/WorldStates/WorldStateMgr.h new file mode 100644 index 00000000000..18ce38afd2f --- /dev/null +++ b/src/server/game/World/WorldStates/WorldStateMgr.h @@ -0,0 +1,50 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 WorldStateMgr_h__ +#define WorldStateMgr_h__ + +#include "Define.h" +#include "WorldStateDefines.h" + +class Map; + +namespace WorldPackets::WorldState +{ + class InitWorldStates; +} + +class TC_GAME_API WorldStateMgr +{ +public: + static WorldStateMgr* instance(); + + void LoadFromDB(); + + WorldStateTemplate const* GetWorldStateTemplate(int32 worldStateId) const; + + int32 GetValue(int32 worldStateId, Map const* map) const; + void SetValue(int32 worldStateId, int32 value, Map* map); + + WorldStateValueContainer GetInitialWorldStatesForMap(Map const* map) const; + + void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& initWorldStates, Map const* map) const; +}; + +#define sWorldStateMgr WorldStateMgr::instance() + +#endif |