Core/Conditions: Implemented conditions for spawn groups

This commit is contained in:
Shauren
2022-06-24 22:20:34 +02:00
parent e2cf6d68fc
commit 5d27939667
7 changed files with 188 additions and 81 deletions

View File

@@ -28,6 +28,7 @@
#include "GameObject.h"
#include "GameTime.h"
#include "Group.h"
#include "InstanceScenario.h"
#include "InstanceScript.h"
#include "Item.h"
#include "LanguageMgr.h"
@@ -43,7 +44,6 @@
#include "RaceMask.h"
#include "Realm.h"
#include "ReputationMgr.h"
#include "Scenario.h"
#include "ScriptMgr.h"
#include "Spell.h"
#include "SpellAuras.h"
@@ -88,7 +88,8 @@ char const* const ConditionMgr::StaticSourceTypeData[CONDITION_SOURCE_TYPE_MAX]
"ConversationLine",
"AreaTrigger Client Triggered",
"Trainer Spell",
"Object Visibility (by ID)"
"Object Visibility (by ID)",
"Spawn Group"
};
ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[CONDITION_MAX] =
@@ -151,24 +152,109 @@ ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[COND
{ "Scene In Progress", true, false, false },
};
ConditionSourceInfo::ConditionSourceInfo(WorldObject* target0, WorldObject* target1, WorldObject* target2)
{
mConditionTargets[0] = target0;
mConditionTargets[1] = target1;
mConditionTargets[2] = target2;
mConditionMap = target0 ? target0->GetMap() : nullptr;
mLastFailedCondition = nullptr;
}
ConditionSourceInfo::ConditionSourceInfo(Map const* map)
{
std::fill(std::begin(mConditionTargets), std::end(mConditionTargets), nullptr);
mConditionMap = map;
mLastFailedCondition = nullptr;
}
// Checks if object meets the condition
// Can have CONDITION_SOURCE_TYPE_NONE && !mReferenceId if called from a special event (ie: SmartAI)
bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
{
ASSERT(ConditionTarget < MAX_CONDITION_TARGETS);
WorldObject* object = sourceInfo.mConditionTargets[ConditionTarget];
// object not present, return false
if (!object)
{
TC_LOG_DEBUG("condition", "Condition object not found for %s", ToString().c_str());
return false;
}
Map const* map = sourceInfo.mConditionMap;
bool condMeets = false;
bool needsObject = false;
switch (ConditionType)
{
case CONDITION_NONE:
condMeets = true; // empty condition, always met
break;
case CONDITION_ACTIVE_EVENT:
condMeets = sGameEventMgr->IsActiveEvent(ConditionValue1);
break;
case CONDITION_INSTANCE_INFO:
{
if (map->IsDungeon())
{
if (InstanceScript const* instance = ((InstanceMap*)map)->GetInstanceScript())
{
switch (ConditionValue3)
{
case INSTANCE_INFO_DATA:
condMeets = instance->GetData(ConditionValue1) == ConditionValue2;
break;
//case INSTANCE_INFO_GUID_DATA:
// condMeets = instance->GetGuidData(ConditionValue1) == ObjectGuid(uint64(ConditionValue2));
// break;
case INSTANCE_INFO_BOSS_STATE:
condMeets = instance->GetBossState(ConditionValue1) == EncounterState(ConditionValue2);
break;
case INSTANCE_INFO_DATA64:
condMeets = instance->GetData64(ConditionValue1) == ConditionValue2;
break;
default:
condMeets = false;
break;
}
}
}
break;
}
case CONDITION_MAPID:
condMeets = map->GetId() == ConditionValue1;
break;
case CONDITION_WORLD_STATE:
{
condMeets = ConditionValue2 == sWorld->getWorldState(ConditionValue1);
break;
}
case CONDITION_REALM_ACHIEVEMENT:
{
AchievementEntry const* achievement = sAchievementStore.LookupEntry(ConditionValue1);
if (achievement && sAchievementMgr->IsRealmCompleted(achievement))
condMeets = true;
break;
}
case CONDITION_DIFFICULTY_ID:
{
condMeets = map->GetDifficultyID() == ConditionValue1;
break;
}
case CONDITION_SCENARIO_STEP:
{
if (InstanceMap const* instanceMap = map->ToInstanceMap())
if (Scenario const* scenario = instanceMap->GetInstanceScenario())
if (ScenarioStepEntry const* step = scenario->GetStep())
condMeets = step->ID == ConditionValue1;
break;
}
default:
needsObject = true;
break;
}
WorldObject* object = sourceInfo.mConditionTargets[ConditionTarget];
// object not present, return false
if (needsObject && !object)
{
TC_LOG_DEBUG("condition", "Condition object not found for %s", ToString().c_str());
return false;
}
switch (ConditionType)
{
case CONDITION_AURA:
{
if (Unit* unit = object->ToUnit())
@@ -273,38 +359,6 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
}
break;
}
case CONDITION_ACTIVE_EVENT:
condMeets = sGameEventMgr->IsActiveEvent(ConditionValue1);
break;
case CONDITION_INSTANCE_INFO:
{
Map* map = object->GetMap();
if (map->IsDungeon())
{
if (InstanceScript const* instance = ((InstanceMap*)map)->GetInstanceScript())
{
switch (ConditionValue3)
{
case INSTANCE_INFO_DATA:
condMeets = instance->GetData(ConditionValue1) == ConditionValue2;
break;
//case INSTANCE_INFO_GUID_DATA:
// condMeets = instance->GetGuidData(ConditionValue1) == ObjectGuid(uint64(ConditionValue2));
// break;
case INSTANCE_INFO_BOSS_STATE:
condMeets = instance->GetBossState(ConditionValue1) == EncounterState(ConditionValue2);
break;
case INSTANCE_INFO_DATA64:
condMeets = instance->GetData64(ConditionValue1) == ConditionValue2;
break;
}
}
}
break;
}
case CONDITION_MAPID:
condMeets = object->GetMapId() == ConditionValue1;
break;
case CONDITION_AREAID:
condMeets = object->GetAreaId() == ConditionValue1;
break;
@@ -434,11 +488,6 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
condMeets = CompareValues(static_cast<ComparisionType>(ConditionValue2), unit->GetHealthPct(), static_cast<float>(ConditionValue1));
break;
}
case CONDITION_WORLD_STATE:
{
condMeets = ConditionValue2 == sWorld->getWorldState(ConditionValue1);
break;
}
case CONDITION_PHASEID:
{
condMeets = object->GetPhaseShift().HasPhase(ConditionValue1);
@@ -462,13 +511,6 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
condMeets = creature->GetCreatureTemplate()->type == ConditionValue1;
break;
}
case CONDITION_REALM_ACHIEVEMENT:
{
AchievementEntry const* achievement = sAchievementStore.LookupEntry(ConditionValue1);
if (achievement && sAchievementMgr->IsRealmCompleted(achievement))
condMeets = true;
break;
}
case CONDITION_IN_WATER:
{
if (Unit* unit = object->ToUnit())
@@ -553,11 +595,6 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
}
break;
}
case CONDITION_DIFFICULTY_ID:
{
condMeets = object->GetMap()->GetDifficultyID() == ConditionValue1;
break;
}
case CONDITION_GAMEMASTER:
{
if (Player* player = object->ToPlayer())
@@ -577,13 +614,6 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
static_cast<uint8>(ConditionValue2));
break;
}
case CONDITION_SCENARIO_STEP:
{
if (Scenario const* scenario = object->GetScenario())
if (ScenarioStepEntry const* step = scenario->GetStep())
condMeets = step->ID == ConditionValue1;
break;
}
case CONDITION_SCENE_IN_PROGRESS:
{
if (Player* player = object->ToPlayer())
@@ -591,7 +621,6 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
break;
}
default:
condMeets = false;
break;
}
@@ -1002,6 +1031,32 @@ bool ConditionMgr::CanHaveSourceIdSet(ConditionSourceType sourceType)
return (sourceType == CONDITION_SOURCE_TYPE_SMART_EVENT);
}
bool ConditionMgr::CanHaveConditionType(ConditionSourceType sourceType, ConditionTypes conditionType)
{
switch (sourceType)
{
case CONDITION_SOURCE_TYPE_SPAWN_GROUP:
switch (conditionType)
{
case CONDITION_NONE:
case CONDITION_ACTIVE_EVENT:
case CONDITION_INSTANCE_INFO:
case CONDITION_MAPID:
case CONDITION_WORLD_STATE:
case CONDITION_REALM_ACHIEVEMENT:
case CONDITION_DIFFICULTY_ID:
case CONDITION_SCENARIO_STEP:
return true;
default:
return false;
}
break;
default:
break;
}
return true;
}
bool ConditionMgr::IsObjectMeetingNotGroupedConditions(ConditionSourceType sourceType, uint32 entry, ConditionSourceInfo& sourceInfo) const
{
if (sourceType > CONDITION_SOURCE_TYPE_NONE && sourceType < CONDITION_SOURCE_TYPE_MAX)
@@ -1023,6 +1078,12 @@ bool ConditionMgr::IsObjectMeetingNotGroupedConditions(ConditionSourceType sourc
return IsObjectMeetingNotGroupedConditions(sourceType, entry, conditionSource);
}
bool ConditionMgr::IsMapMeetingNotGroupedConditions(ConditionSourceType sourceType, uint32 entry, Map const* map) const
{
ConditionSourceInfo conditionSource(map);
return IsObjectMeetingNotGroupedConditions(sourceType, entry, conditionSource);
}
bool ConditionMgr::HasConditionsForNotGroupedEntry(ConditionSourceType sourceType, uint32 entry) const
{
if (sourceType > CONDITION_SOURCE_TYPE_NONE && sourceType < CONDITION_SOURCE_TYPE_MAX)
@@ -2097,6 +2158,21 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) const
}
break;
}
case CONDITION_SOURCE_TYPE_SPAWN_GROUP:
{
SpawnGroupTemplateData const* spawnGroup = sObjectMgr->GetSpawnGroupData(cond->SourceEntry);
if (!spawnGroup)
{
TC_LOG_ERROR("sql.sql", "%s SourceEntry in `condition` table, does not exist in `spawn_group_template`, ignoring.", cond->ToString().c_str());
return false;
}
if (spawnGroup->flags & (SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_MANUAL_SPAWN))
{
TC_LOG_ERROR("sql.sql", "%s in `spawn_group_template` table cannot have SPAWNGROUP_FLAG_SYSTEM or SPAWNGROUP_FLAG_MANUAL_SPAWN flags, ignoring.", cond->ToString().c_str());
return false;
}
break;
}
default:
TC_LOG_ERROR("sql.sql", "%s Invalid ConditionSourceType in `condition` table, ignoring.", cond->ToString().c_str());
return false;

View File

@@ -27,6 +27,7 @@
#include <vector>
class Creature;
class Map;
class Player;
class Unit;
class WorldObject;
@@ -179,7 +180,8 @@ enum ConditionSourceType
CONDITION_SOURCE_TYPE_AREATRIGGER_CLIENT_TRIGGERED = 30,
CONDITION_SOURCE_TYPE_TRAINER_SPELL = 31,
CONDITION_SOURCE_TYPE_OBJECT_ID_VISIBILITY = 32,
CONDITION_SOURCE_TYPE_MAX = 33 // MAX
CONDITION_SOURCE_TYPE_SPAWN_GROUP = 33,
CONDITION_SOURCE_TYPE_MAX = 34 // MAX
};
enum RelationType
@@ -209,14 +211,10 @@ enum MaxConditionTargets
struct TC_GAME_API ConditionSourceInfo
{
WorldObject* mConditionTargets[MAX_CONDITION_TARGETS]; // an array of targets available for conditions
Map const* mConditionMap;
Condition const* mLastFailedCondition;
ConditionSourceInfo(WorldObject* target0, WorldObject* target1 = nullptr, WorldObject* target2 = nullptr)
{
mConditionTargets[0] = target0;
mConditionTargets[1] = target1;
mConditionTargets[2] = target2;
mLastFailedCondition = nullptr;
}
ConditionSourceInfo(WorldObject* target0, WorldObject* target1 = nullptr, WorldObject* target2 = nullptr);
ConditionSourceInfo(Map const* map);
};
struct TC_GAME_API Condition
@@ -290,8 +288,10 @@ class TC_GAME_API ConditionMgr
bool IsObjectMeetToConditions(ConditionSourceInfo& sourceInfo, ConditionContainer const& conditions) const;
static bool CanHaveSourceGroupSet(ConditionSourceType sourceType);
static bool CanHaveSourceIdSet(ConditionSourceType sourceType);
static bool CanHaveConditionType(ConditionSourceType sourceType, ConditionTypes conditionType);
bool IsObjectMeetingNotGroupedConditions(ConditionSourceType sourceType, uint32 entry, ConditionSourceInfo& sourceInfo) const;
bool IsObjectMeetingNotGroupedConditions(ConditionSourceType sourceType, uint32 entry, WorldObject* target0, WorldObject* target1 = nullptr, WorldObject* target2 = nullptr) const;
bool IsMapMeetingNotGroupedConditions(ConditionSourceType sourceType, uint32 entry, Map const* map) const;
bool HasConditionsForNotGroupedEntry(ConditionSourceType sourceType, uint32 entry) const;
bool IsObjectMeetingSpellClickConditions(uint32 creatureId, uint32 spellId, WorldObject* clicker, WorldObject* target) const;
ConditionContainer const* GetConditionsForSpellClickEvent(uint32 creatureId, uint32 spellId) const;

View File

@@ -2885,7 +2885,10 @@ void ObjectMgr::LoadSpawnGroups()
{
SpawnGroupTemplateData& groupTemplate = it->second;
if (groupTemplate.mapId == SPAWNGROUP_MAP_UNSET)
{
groupTemplate.mapId = data->mapId;
_spawnGroupsByMap[data->mapId].push_back(groupId);
}
else if (groupTemplate.mapId != data->mapId && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
{
TC_LOG_ERROR("sql.sql", "Spawn group %u has map ID %u, but spawn (%u," UI64FMTD ") has map id %u - spawn NOT added to group!", groupId, groupTemplate.mapId, uint32(spawnType), spawnId, data->mapId);

View File

@@ -1455,6 +1455,7 @@ class TC_GAME_API ObjectMgr
SpawnGroupTemplateData const* GetDefaultSpawnGroup() const { return &_spawnGroupDataStore.at(0); }
SpawnGroupTemplateData const* GetLegacySpawnGroup() const { return &_spawnGroupDataStore.at(1); }
Trinity::IteratorPair<SpawnGroupLinkContainer::const_iterator> GetSpawnMetadataForGroup(uint32 groupId) const { return Trinity::Containers::MapEqualRange(_spawnGroupMapStore, groupId); }
std::vector<uint32> const* GetSpawnGroupsForMap(uint32 mapId) const { auto it = _spawnGroupsByMap.find(mapId); return it != _spawnGroupsByMap.end() ? &it->second : nullptr; }
std::vector<InstanceSpawnGroupInfo> const* GetInstanceSpawnGroupsForMap(uint32 mapId) const { auto it = _instanceSpawnGroupStore.find(mapId); return it != _instanceSpawnGroupStore.end() ? &it->second : nullptr; }
MailLevelReward const* GetMailLevelReward(uint8 level, uint8 race) const
@@ -1922,6 +1923,7 @@ class TC_GAME_API ObjectMgr
GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore;
GameObjectOverrideContainer _gameObjectOverrideStore;
SpawnGroupDataContainer _spawnGroupDataStore;
std::unordered_map<uint32, std::vector<uint32>> _spawnGroupsByMap;
SpawnGroupLinkContainer _spawnGroupMapStore;
InstanceSpawnGroupContainer _instanceSpawnGroupStore;
/// Stores temp summon data grouped by summoner's entry, summoner's type and group id

View File

@@ -853,6 +853,7 @@ void Map::Update(uint32 t_diff)
if (_respawnCheckTimer <= t_diff)
{
ProcessRespawns();
UpdateSpawnGroupConditions();
_respawnCheckTimer = sWorld->getIntConfig(CONFIG_RESPAWN_MINCHECKINTERVALMS);
}
else
@@ -3640,6 +3641,28 @@ bool Map::IsSpawnGroupActive(uint32 groupId) const
return (_toggledSpawnGroupIds.find(groupId) != _toggledSpawnGroupIds.end()) != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN);
}
void Map::UpdateSpawnGroupConditions()
{
std::vector<uint32> const* spawnGroups = sObjectMgr->GetSpawnGroupsForMap(GetId());
if (!spawnGroups)
return;
for (uint32 spawnGroupId : *spawnGroups)
{
bool isActive = IsSpawnGroupActive(spawnGroupId);
bool shouldBeActive = sConditionMgr->IsMapMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SPAWN_GROUP, spawnGroupId, this);
if (isActive == shouldBeActive)
continue;
if (shouldBeActive)
SpawnGroupSpawn(spawnGroupId);
else if (ASSERT_NOTNULL(GetSpawnGroupData(spawnGroupId))->flags & SPAWNGROUP_FLAG_DESPAWN_ON_CONDITION_FAILURE)
SpawnGroupDespawn(spawnGroupId);
else
SetSpawnGroupInactive(spawnGroupId);
}
}
void Map::AddFarSpellCallback(FarSpellCallback&& callback)
{
_farSpellCallbacks.Enqueue(new FarSpellCallback(std::move(callback)));

View File

@@ -876,6 +876,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
}
void SetSpawnGroupActive(uint32 groupId, bool state);
void UpdateSpawnGroupConditions();
std::unordered_set<uint32> _toggledSpawnGroupIds;
uint32 _respawnCheckTimer;

View File

@@ -50,14 +50,16 @@ enum SpawnObjectTypeMask
enum SpawnGroupFlags
{
SPAWNGROUP_FLAG_NONE = 0x00,
SPAWNGROUP_FLAG_SYSTEM = 0x01,
SPAWNGROUP_FLAG_COMPATIBILITY_MODE = 0x02,
SPAWNGROUP_FLAG_MANUAL_SPAWN = 0x04,
SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE = 0x08,
SPAWNGROUP_FLAG_ESCORTQUESTNPC = 0x10,
SPAWNGROUP_FLAG_NONE = 0x00,
SPAWNGROUP_FLAG_SYSTEM = 0x01,
SPAWNGROUP_FLAG_COMPATIBILITY_MODE = 0x02,
SPAWNGROUP_FLAG_MANUAL_SPAWN = 0x04,
SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE = 0x08,
SPAWNGROUP_FLAG_ESCORTQUESTNPC = 0x10,
SPAWNGROUP_FLAG_DESPAWN_ON_CONDITION_FAILURE = 0x20,
SPAWNGROUP_FLAGS_ALL = (SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_COMPATIBILITY_MODE | SPAWNGROUP_FLAG_MANUAL_SPAWN | SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE | SPAWNGROUP_FLAG_ESCORTQUESTNPC)
SPAWNGROUP_FLAGS_ALL = (SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_COMPATIBILITY_MODE | SPAWNGROUP_FLAG_MANUAL_SPAWN
| SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE | SPAWNGROUP_FLAG_ESCORTQUESTNPC | SPAWNGROUP_FLAG_DESPAWN_ON_CONDITION_FAILURE)
};
struct SpawnGroupTemplateData