Scripts/InstanceScript: Implement database framework for managing spawn groups based on boss state. (#20103)

This commit is contained in:
Treeston
2017-08-04 00:23:40 +02:00
committed by GitHub
parent 2644fa703a
commit 84590be26d
7 changed files with 169 additions and 27 deletions

View File

@@ -0,0 +1,10 @@
--
DROP TABLE IF EXISTS `instance_spawn_groups`;
CREATE TABLE `instance_spawn_groups` (
`instanceMapId` smallint(5) unsigned not null,
`bossStateId` tinyint unsigned not null,
`bossStates` tinyint unsigned not null,
`spawnGroupId` int unsigned not null,
`flags` tinyint unsigned not null,
PRIMARY KEY (`instanceMapId`,`bossStateId`,`spawnGroupId`,`bossStates`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

View File

@@ -30,6 +30,7 @@
#include "GroupMgr.h"
#include "GuildMgr.h"
#include "InstanceSaveMgr.h"
#include "InstanceScript.h"
#include "Language.h"
#include "LFGMgr.h"
#include "Log.h"
@@ -2322,6 +2323,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();
auto& 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);
@@ -6726,19 +6784,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;
}

View File

@@ -422,6 +422,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;
@@ -534,6 +549,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;
@@ -1129,6 +1145,7 @@ class TC_GAME_API ObjectMgr
void LoadGameObjects();
void LoadSpawnGroupTemplates();
void LoadSpawnGroups();
void LoadInstanceSpawnGroups();
void LoadItemTemplates();
void LoadItemLocales();
void LoadItemSetNames();
@@ -1215,11 +1232,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(uint32 level, uint32 raceMask) const
{
@@ -1609,6 +1628,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;

View File

@@ -46,7 +46,7 @@ BossBoundaryData::~BossBoundaryData()
delete it->Boundary;
}
InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0)
InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0), _instanceSpawnGroups(sObjectMgr->GetSpawnGroupsForInstance(map->GetId()))
{
#ifdef TRINITY_API_USE_DYNAMIC_LINKING
uint32 scriptId = sObjectMgr->GetInstanceTemplate(map->GetId())->ScriptId;
@@ -186,27 +186,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());
@@ -236,6 +215,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());
@@ -310,7 +343,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
@@ -337,6 +370,7 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
if (Creature* minion = instance->GetCreature(*i))
UpdateMinionState(minion, state);
UpdateSpawnGroups();
return true;
}
return false;
@@ -347,6 +381,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)
@@ -397,6 +438,7 @@ void InstanceScript::ReadSaveDataBossStates(std::istringstream& data)
if (buff < TO_BE_DECIDED)
SetBossState(bossId, EncounterState(buff));
}
UpdateSpawnGroups();
}
std::string InstanceScript::GetSaveData()

View File

@@ -34,6 +34,7 @@
class AreaBoundary;
class Creature;
class GameObject;
struct InstanceSpawnGroupInfo;
class Map;
class ModuleReference;
class Player;
@@ -156,7 +157,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
@@ -267,6 +271,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);
@@ -293,6 +299,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;
#ifdef TRINITY_API_USE_DYNAMIC_LINKING
// Strong reference to the associated script module

View File

@@ -3735,6 +3735,8 @@ void InstanceMap::CreateInstanceData(bool load)
}
}
}
else
i_data->Create();
}
/*

View File

@@ -1705,6 +1705,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();