diff options
author | Treeston <treeston.mmoc@gmail.com> | 2017-08-04 00:23:40 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2020-08-22 13:48:44 +0200 |
commit | 608c9aaabfcdbaf09edd11cdf268c243b3e69478 (patch) | |
tree | 11acd56239005fb5d9c96b733976830cf4365e7e /src/server | |
parent | acb4934f766321efbf90d0c6716f2df44c47ef16 (diff) |
Scripts/InstanceScript: Implement database framework for managing spawn groups based on boss state. (#20103)
(cherry picked from commit 84590be26d6b3c56b95a804cbe889826186dd8a2)
Diffstat (limited to 'src/server')
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 64 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 20 | ||||
-rw-r--r-- | src/server/game/Instances/InstanceScript.cpp | 88 | ||||
-rw-r--r-- | src/server/game/Instances/InstanceScript.h | 9 | ||||
-rw-r--r-- | src/server/game/Maps/Map.cpp | 2 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 3 |
6 files changed, 159 insertions, 27 deletions
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 0728465d34c..0a64d4b2f4f 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -32,6 +32,7 @@ #include "GossipDef.h" #include "GroupMgr.h" #include "GuildMgr.h" +#include "InstanceScript.h" #include "Item.h" #include "LFGMgr.h" #include "Log.h" @@ -2642,6 +2643,63 @@ void ObjectMgr::LoadSpawnGroups() TC_LOG_INFO("server.loading", ">> Loaded %u spawn group members in %u ms", numMembers, GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadInstanceSpawnGroups() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups"); + + if (!result) + { + TC_LOG_ERROR("server.loading", ">> Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty."); + return; + } + + uint32 n = 0; + do + { + Field* fields = result->Fetch(); + uint32 const spawnGroupId = fields[3].GetUInt32(); + auto it = _spawnGroupDataStore.find(spawnGroupId); + if (it == _spawnGroupDataStore.end() || (it->second.flags & SPAWNGROUP_FLAG_SYSTEM)) + { + TC_LOG_ERROR("server.loading", "Invalid spawn group %u specified for instance %u. Skipped.", spawnGroupId, fields[0].GetUInt16()); + continue; + } + + uint16 const instanceMapId = fields[0].GetUInt16(); + std::vector<InstanceSpawnGroupInfo>& vector = _instanceSpawnGroupStore[instanceMapId]; + vector.emplace_back(); + InstanceSpawnGroupInfo& info = vector.back(); + info.SpawnGroupId = spawnGroupId; + info.BossStateId = fields[1].GetUInt8(); + + uint8 const ALL_STATES = (1 << TO_BE_DECIDED) - 1; + uint8 const states = fields[2].GetUInt8(); + if (states & ~ALL_STATES) + { + info.BossStates = states & ALL_STATES; + TC_LOG_ERROR("server.loading", "Instance spawn group (%u,%u) had invalid boss state mask %u - truncated to %u.", instanceMapId, spawnGroupId, states, info.BossStates); + } + else + info.BossStates = states; + + uint8 const flags = fields[4].GetUInt8(); + if (flags & ~InstanceSpawnGroupInfo::FLAG_ALL) + { + info.Flags = flags & InstanceSpawnGroupInfo::FLAG_ALL; + TC_LOG_ERROR("server.loading", "Instance spawn group (%u,%u) had invalid flags %u - truncated to %u.", instanceMapId, spawnGroupId, flags, info.Flags); + } + else + info.Flags = flags; + + ++n; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u instance spawn groups in %u ms", n, GetMSTimeDiffToNow(oldMSTime)); +} + void ObjectMgr::OnDeleteSpawnData(SpawnData const* data) { auto templateIt = _spawnGroupDataStore.find(data->spawnGroupData->groupId); @@ -7053,19 +7111,19 @@ bool ObjectMgr::SpawnGroupSpawn(uint32 groupId, Map* map, bool ignoreRespawn, bo auto itr = _spawnGroupDataStore.find(groupId); if (itr == _spawnGroupDataStore.end() || itr->second.flags & SPAWNGROUP_FLAG_SYSTEM) { - TC_LOG_ERROR("maps", "Tried to despawn non-existing (or system) spawn group %u. Blocked.", groupId); + TC_LOG_ERROR("maps", "Tried to spawn non-existing (or system) spawn group %u. Blocked.", groupId); return false; } if (!map) { - TC_LOG_ERROR("maps", "Tried to despawn creature group %u, but no map was supplied. Blocked.", groupId); + TC_LOG_ERROR("maps", "Tried to spawn creature group %u, but no map was supplied. Blocked.", groupId); return false; } if (itr->second.mapId != map->GetId()) { - TC_LOG_ERROR("maps", "Tried to despawn creature group %u, but supplied map is %u, creature group has map %u. Blocked.", groupId, map->GetId(), itr->second.mapId); + TC_LOG_ERROR("maps", "Tried to spawn creature group %u, but supplied map is %u, creature group has map %u. Blocked.", groupId, map->GetId(), itr->second.mapId); return false; } diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index b863b8b8fc7..181a191ba92 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -428,6 +428,21 @@ std::string GetScriptsTableNameByType(ScriptsType type); ScriptMapMap* GetScriptsMapByType(ScriptsType type); std::string GetScriptCommandName(ScriptCommands command); +struct TC_GAME_API InstanceSpawnGroupInfo +{ + enum + { + FLAG_ACTIVATE_SPAWN = 0x01, + FLAG_BLOCK_SPAWN = 0x02, + + FLAG_ALL = (FLAG_ACTIVATE_SPAWN | FLAG_BLOCK_SPAWN) + }; + uint8 BossStateId; + uint8 BossStates; + uint32 SpawnGroupId; + uint8 Flags; +}; + struct TC_GAME_API SpellClickInfo { uint32 spellId; @@ -496,6 +511,7 @@ typedef std::unordered_map<ObjectGuid::LowType, GameObjectAddon> GameObjectAddon typedef std::unordered_map<uint32, std::vector<uint32>> GameObjectQuestItemMap; typedef std::unordered_map<uint32, SpawnGroupTemplateData> SpawnGroupDataContainer; typedef std::multimap<uint32, SpawnData const*> SpawnGroupLinkContainer; +typedef std::unordered_map<uint16, std::vector<InstanceSpawnGroupInfo>> InstanceSpawnGroupContainer; typedef std::map<TempSummonGroupKey, std::vector<TempSummonData>> TempSummonDataContainer; typedef std::unordered_map<uint32, CreatureLocale> CreatureLocaleContainer; typedef std::unordered_map<uint32, GameObjectLocale> GameObjectLocaleContainer; @@ -1249,6 +1265,7 @@ class TC_GAME_API ObjectMgr void LoadGameObjects(); void LoadSpawnGroupTemplates(); void LoadSpawnGroups(); + void LoadInstanceSpawnGroups(); void LoadItemTemplates(); void LoadItemTemplateAddon(); void LoadItemScriptNames(); @@ -1359,11 +1376,13 @@ class TC_GAME_API ObjectMgr bool SpawnGroupSpawn(uint32 groupId, Map* map, bool ignoreRespawn = false, bool force = false, std::vector<WorldObject*>* spawnedObjects = nullptr); bool SpawnGroupDespawn(uint32 groupId, Map* map, bool deleteRespawnTimes = false); + SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const { auto it = _spawnGroupDataStore.find(groupId); return it != _spawnGroupDataStore.end() ? &it->second : nullptr; } void SetSpawnGroupActive(uint32 groupId, bool state) { auto it = _spawnGroupDataStore.find(groupId); if (it != _spawnGroupDataStore.end()) it->second.isActive = state; } bool IsSpawnGroupActive(uint32 groupId) const { auto it = _spawnGroupDataStore.find(groupId); return (it != _spawnGroupDataStore.end()) && it->second.isActive; } SpawnGroupTemplateData const* GetDefaultSpawnGroup() const { return &_spawnGroupDataStore.at(0); } SpawnGroupTemplateData const* GetLegacySpawnGroup() const { return &_spawnGroupDataStore.at(1); } Trinity::IteratorPair<SpawnGroupLinkContainer::const_iterator> GetSpawnDataForGroup(uint32 groupId) const { return Trinity::Containers::MapEqualRange(_spawnGroupMapStore, groupId); } + std::vector<InstanceSpawnGroupInfo> const* GetSpawnGroupsForInstance(uint32 instanceId) const { auto it = _instanceSpawnGroupStore.find(instanceId); return it != _instanceSpawnGroupStore.end() ? &it->second : nullptr; } MailLevelReward const* GetMailLevelReward(uint8 level, uint8 race) const { @@ -1792,6 +1811,7 @@ class TC_GAME_API ObjectMgr GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore; SpawnGroupDataContainer _spawnGroupDataStore; SpawnGroupLinkContainer _spawnGroupMapStore; + InstanceSpawnGroupContainer _instanceSpawnGroupStore; /// Stores temp summon data grouped by summoner's entry, summoner's type and group id TempSummonDataContainer _tempSummonDataStore; std::unordered_map<int32 /*choiceId*/, PlayerChoice> _playerChoices; diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp index 3b85c3dcac9..c3cccd75d63 100644 --- a/src/server/game/Instances/InstanceScript.cpp +++ b/src/server/game/Instances/InstanceScript.cpp @@ -48,7 +48,7 @@ BossBoundaryData::~BossBoundaryData() delete it->Boundary; } -InstanceScript::InstanceScript(InstanceMap* map) : instance(map), completedEncounters(0), +InstanceScript::InstanceScript(InstanceMap* map) : instance(map), completedEncounters(0), _instanceSpawnGroups(sObjectMgr->GetSpawnGroupsForInstance(map->GetId())), _entranceId(0), _temporaryEntranceId(0), _combatResurrectionTimer(0), _combatResurrectionCharges(0), _combatResurrectionTimerStarted(false) { #ifdef TRINITY_API_USE_DYNAMIC_LINKING @@ -193,27 +193,6 @@ void InstanceScript::LoadObjectData(ObjectData const* data, ObjectInfoMap& objec } } -void InstanceScript::UpdateMinionState(Creature* minion, EncounterState state) -{ - switch (state) - { - case NOT_STARTED: - if (!minion->IsAlive()) - minion->Respawn(); - else if (minion->IsInCombat()) - minion->AI()->EnterEvadeMode(); - break; - case IN_PROGRESS: - if (!minion->IsAlive()) - minion->Respawn(); - else if (!minion->GetVictim()) - minion->AI()->DoZoneInCombat(); - break; - default: - break; - } -} - void InstanceScript::UpdateDoorState(GameObject* door) { DoorInfoMapBounds range = doors.equal_range(door->GetEntry()); @@ -243,6 +222,60 @@ void InstanceScript::UpdateDoorState(GameObject* door) door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY); } +void InstanceScript::UpdateMinionState(Creature* minion, EncounterState state) +{ + switch (state) + { + case NOT_STARTED: + if (!minion->IsAlive()) + minion->Respawn(); + else if (minion->IsInCombat()) + minion->AI()->EnterEvadeMode(); + break; + case IN_PROGRESS: + if (!minion->IsAlive()) + minion->Respawn(); + else if (!minion->GetVictim()) + minion->AI()->DoZoneInCombat(); + break; + default: + break; + } +} + +void InstanceScript::UpdateSpawnGroups() +{ + if (!_instanceSpawnGroups) + return; + enum states { BLOCK, SPAWN, FORCEBLOCK }; + std::unordered_map<uint32, states> newStates; + for (auto it = _instanceSpawnGroups->begin(), end = _instanceSpawnGroups->end(); it != end; ++it) + { + InstanceSpawnGroupInfo const& info = *it; + states& curValue = newStates[info.SpawnGroupId]; // makes sure there's a BLOCK value in the map + if (curValue == FORCEBLOCK) // nothing will change this + continue; + if (!((1 << GetBossState(info.BossStateId)) & info.BossStates)) + continue; + if (info.Flags & InstanceSpawnGroupInfo::FLAG_BLOCK_SPAWN) + curValue = FORCEBLOCK; + else if (info.Flags & InstanceSpawnGroupInfo::FLAG_ACTIVATE_SPAWN) + curValue = SPAWN; + } + for (auto const& pair : newStates) + { + uint32 const groupId = pair.first; + bool const doSpawn = (pair.second == SPAWN); + if (sObjectMgr->IsSpawnGroupActive(groupId) == doSpawn) + continue; // nothing to do here + // if we should spawn group, then spawn it... + if (doSpawn) + sObjectMgr->SpawnGroupSpawn(groupId, instance); + else // otherwise, set it as inactive so it no longer respawns (but don't despawn it) + sObjectMgr->SetSpawnGroupActive(groupId, false); + } +} + BossInfo* InstanceScript::GetBossInfo(uint32 id) { ASSERT(id < bosses.size()); @@ -317,7 +350,7 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state) if (bossInfo->state == TO_BE_DECIDED) // loading { bossInfo->state = state; - //TC_LOG_ERROR("misc", "Inialize boss %u state as %u.", id, (uint32)state); + TC_LOG_DEBUG("scripts", "InstanceScript: Initialize boss %u state as %u.", id, (uint32)state); return false; } else @@ -370,6 +403,7 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state) if (Creature* minion = instance->GetCreature(*i)) UpdateMinionState(minion, state); + UpdateSpawnGroups(); return true; } return false; @@ -380,6 +414,13 @@ bool InstanceScript::_SkipCheckRequiredBosses(Player const* player /*= nullptr*/ return player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES); } +void InstanceScript::Create() +{ + for (size_t i = 0; i < bosses.size(); ++i) + SetBossState(i, NOT_STARTED); + UpdateSpawnGroups(); +} + void InstanceScript::Load(char const* data) { if (!data) @@ -430,6 +471,7 @@ void InstanceScript::ReadSaveDataBossStates(std::istringstream& data) if (buff < TO_BE_DECIDED) SetBossState(bossId, EncounterState(buff)); } + UpdateSpawnGroups(); } std::string InstanceScript::GetSaveData() diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index c382c8aad83..d9ceabb6189 100644 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -38,6 +38,7 @@ class InstanceMap; class ModuleReference; class Player; class Unit; +struct InstanceSpawnGroupInfo; enum CriteriaTypes : uint8; enum CriteriaTimedTypes : uint8; enum EncounterCreditType : uint8; @@ -165,7 +166,10 @@ class TC_GAME_API InstanceScript : public ZoneScript // KEEPING THIS METHOD ONLY FOR BACKWARD COMPATIBILITY !!! virtual void Initialize() { } - // On load + // On instance load, exactly ONE of these methods will ALWAYS be called: + // if we're starting without any saved instance data + virtual void Create(); + // if we're loading existing instance save data virtual void Load(char const* data); // When save is needed, this function generates the data @@ -300,6 +304,8 @@ class TC_GAME_API InstanceScript : public ZoneScript virtual void UpdateDoorState(GameObject* door); void UpdateMinionState(Creature* minion, EncounterState state); + void UpdateSpawnGroups(); + // Exposes private data that should never be modified unless exceptional cases. // Pay very much attention at how the returned BossInfo data is modified to avoid issues. BossInfo* GetBossInfo(uint32 id); @@ -326,6 +332,7 @@ class TC_GAME_API InstanceScript : public ZoneScript ObjectInfoMap _gameObjectInfo; ObjectGuidMap _objectGuids; uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets + std::vector<InstanceSpawnGroupInfo> const* const _instanceSpawnGroups; uint32 _entranceId; uint32 _temporaryEntranceId; uint32 _combatResurrectionTimer; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 984e67d2a5d..6c3794d16db 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -4123,6 +4123,8 @@ void InstanceMap::CreateInstanceData(bool load) } } } + else + i_data->Create(); } /* diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index ad81daa1eac..9cf3e3c65ba 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1868,6 +1868,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Spawn Group Templates..."); sObjectMgr->LoadSpawnGroupTemplates(); + TC_LOG_INFO("server.loading", "Loading instance spawn groups..."); + sObjectMgr->LoadInstanceSpawnGroups(); + TC_LOG_INFO("server.loading", "Loading Creature Data..."); sObjectMgr->LoadCreatures(); |