diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index b6c5d3f4bd..dc50e42153 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -66,7 +66,12 @@ char const* const ConditionMgr::StaticSourceTypeData[CONDITION_SOURCE_TYPE_MAX] "Terrain Swap", "Phase", "Graveyard", - "Spell Area" + "Spell Area", + "ConversationLine", + "AreaTrigger Client Triggered", + "Trainer Spell", + "Object Visibility (by ID)", + "Spawn Group" }; ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[CONDITION_MAX] = @@ -121,24 +126,97 @@ ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[COND { "Quest state mask", true, true, 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 = sWorldStateMgr->GetValue(ConditionValue1, map) == int32(ConditionValue2); + break; + } + case CONDITION_REALM_ACHIEVEMENT: + { + AchievementEntry const* achievement = sAchievementStore.LookupEntry(ConditionValue1); + if (achievement && sAchievementMgr->IsRealmCompleted(achievement)) + condMeets = true; + 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()) @@ -248,38 +326,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; @@ -409,11 +455,6 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const condMeets = CompareValues(static_cast(ConditionValue2), unit->GetHealthPct(), static_cast(ConditionValue1)); break; } - case CONDITION_WORLD_STATE: - { - condMeets = sWorldStateMgr->GetValue(ConditionValue1, object->GetMap()) == int32(ConditionValue2); - break; - } case CONDITION_PHASEID: { condMeets = object->GetPhaseShift().HasPhase(ConditionValue1); @@ -447,13 +488,6 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const condMeets = object->GetPhaseShift().HasVisibleMapId(ConditionValue1); break; } - case CONDITION_REALM_ACHIEVEMENT: - { - AchievementEntry const* achievement = sAchievementMgr->GetAchievement(ConditionValue1); - if (achievement && sAchievementMgr->IsRealmCompleted(achievement)) - condMeets = true; - break; - } case CONDITION_IN_WATER: { if (Unit* unit = object->ToUnit()) @@ -514,7 +548,6 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const break; } default: - condMeets = false; break; } @@ -909,6 +942,30 @@ 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: + 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) @@ -930,6 +987,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) @@ -1900,6 +1963,21 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) const return false; } 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: break; } diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index c2dbb47a47..1246c44eed 100644 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -27,6 +27,7 @@ #include class Creature; +class Map; class Player; class Unit; class WorldObject; @@ -144,7 +145,12 @@ enum ConditionSourceType CONDITION_SOURCE_TYPE_PHASE = 26, CONDITION_SOURCE_TYPE_GRAVEYARD = 27, CONDITION_SOURCE_TYPE_SPELL_AREA = 28, - CONDITION_SOURCE_TYPE_MAX = 29 // MAX + //CONDITION_SOURCE_TYPE_CONVERSATION_LINE = 29, // master only + //CONDITION_SOURCE_TYPE_AREATRIGGER_CLIENT_TRIGGERED = 30, // master only + //CONDITION_SOURCE_TYPE_TRAINER_SPELL = 31, // master only + //CONDITION_SOURCE_TYPE_OBJECT_ID_VISIBILITY = 32, // master only + CONDITION_SOURCE_TYPE_SPAWN_GROUP = 33, + CONDITION_SOURCE_TYPE_MAX = 34 // MAX }; enum RelationType @@ -174,14 +180,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 @@ -254,8 +256,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; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index bdd4d67602..56dcee2caf 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -2670,7 +2670,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,%u) has map id %u - spawn NOT added to group!", groupId, groupTemplate.mapId, uint32(spawnType), spawnId, data->mapId); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 71e3cbae92..b0d4c13a7b 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1308,6 +1308,7 @@ class TC_GAME_API ObjectMgr SpawnGroupTemplateData const* GetDefaultSpawnGroup() const { return &_spawnGroupDataStore.at(0); } SpawnGroupTemplateData const* GetLegacySpawnGroup() const { return &_spawnGroupDataStore.at(1); } Trinity::IteratorPair GetSpawnMetadataForGroup(uint32 groupId) const { return Trinity::Containers::MapEqualRange(_spawnGroupMapStore, groupId); } + std::vector const* GetSpawnGroupsForMap(uint32 mapId) const { auto it = _spawnGroupsByMap.find(mapId); return it != _spawnGroupsByMap.end() ? &it->second : nullptr; } std::vector 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 @@ -1758,6 +1759,7 @@ class TC_GAME_API ObjectMgr GameObjectTemplateContainer _gameObjectTemplateStore; GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore; SpawnGroupDataContainer _spawnGroupDataStore; + std::unordered_map> _spawnGroupsByMap; SpawnGroupLinkContainer _spawnGroupMapStore; InstanceSpawnGroupContainer _instanceSpawnGroupStore; /// Stores temp summon data grouped by summoner's entry, summoner's type and group id diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index ddcdbf8060..fcd4bca008 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -624,6 +624,7 @@ void Map::Update(uint32 t_diff) if (_respawnCheckTimer <= t_diff) { ProcessRespawns(); + UpdateSpawnGroupConditions(); _respawnCheckTimer = sWorld->getIntConfig(CONFIG_RESPAWN_MINCHECKINTERVALMS); } else @@ -2191,6 +2192,28 @@ bool Map::IsSpawnGroupActive(uint32 groupId) const return (_toggledSpawnGroupIds.find(groupId) != _toggledSpawnGroupIds.end()) != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN); } +void Map::UpdateSpawnGroupConditions() +{ + std::vector 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))); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 16d58ff9c7..d15e45e54d 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -707,6 +707,7 @@ class TC_GAME_API Map : public GridRefManager } void SetSpawnGroupActive(uint32 groupId, bool state); + void UpdateSpawnGroupConditions(); std::unordered_set _toggledSpawnGroupIds; uint32 _respawnCheckTimer; diff --git a/src/server/game/Maps/SpawnData.h b/src/server/game/Maps/SpawnData.h index 1c0ef20d32..595265d2ca 100644 --- a/src/server/game/Maps/SpawnData.h +++ b/src/server/game/Maps/SpawnData.h @@ -45,14 +45,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