mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Scripts/InstanceScript: Implement database framework for managing spawn groups based on boss state. (#20103)
(cherry picked from commit 84590be26d)
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
--
|
||||
DROP TABLE IF EXISTS `instance_spawn_groups`;
|
||||
CREATE TABLE `instance_spawn_groups` (
|
||||
`instanceMapId` smallint(5) unsigned not null,
|
||||
`bossStateId` tinyint(3) unsigned not null,
|
||||
`bossStates` tinyint(3) unsigned not null,
|
||||
`spawnGroupId` int(10) unsigned not null,
|
||||
`flags` tinyint(3) unsigned not null,
|
||||
PRIMARY KEY (`instanceMapId`,`bossStateId`,`spawnGroupId`,`bossStates`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -4123,6 +4123,8 @@ void InstanceMap::CreateInstanceData(bool load)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
i_data->Create();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user