diff options
author | Shauren <shauren.trinity@gmail.com> | 2024-05-26 22:29:57 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2024-05-27 18:53:47 +0200 |
commit | 1c3268155d0165e150d239c3a808d5a8dddeae18 (patch) | |
tree | 764d238d71e7349d6001edd21dec25cefaf7d4a6 /src | |
parent | b070e63fa867f7f25e73e9ef3aafbe18902a50e9 (diff) |
Core/AreaTriggers: Implement height check for polygon db2 areatriggers
Diffstat (limited to 'src')
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.cpp | 4 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.h | 3 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2LoadInfo.h | 13 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 32 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 11 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Structure.h | 10 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCEnums.h | 19 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 5 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 44 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 10 | ||||
-rw-r--r-- | src/server/game/Handlers/MiscHandler.cpp | 2 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 3 |
12 files changed, 118 insertions, 38 deletions
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index e852d0e4f25..73ac2c07afd 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -1235,6 +1235,10 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_PATH_NODE, "SELECT ID, PathID, Sequence, LocationID FROM path_node WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_PATH_NODE, "SELECT MAX(ID) + 1 FROM path_node", CONNECTION_SYNCH); + // PathProperty.db2 + PrepareStatement(HOTFIX_SEL_PATH_PROPERTY, "SELECT ID, PathID, PropertyIndex, Value FROM path_property WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_PATH_PROPERTY, "SELECT MAX(ID) + 1 FROM path_property", CONNECTION_SYNCH); + // Phase.db2 PrepareStatement(HOTFIX_SEL_PHASE, "SELECT ID, Flags FROM phase WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_PHASE, "SELECT MAX(ID) + 1 FROM phase", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 5ffe7d2560c..03b0631c45c 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -715,6 +715,9 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_PATH_NODE, HOTFIX_SEL_PATH_NODE_MAX_ID, + HOTFIX_SEL_PATH_PROPERTY, + HOTFIX_SEL_PATH_PROPERTY_MAX_ID, + HOTFIX_SEL_PHASE, HOTFIX_SEL_PHASE_MAX_ID, diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index 0040ad734cf..0e2207b5fe9 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -4010,6 +4010,19 @@ struct PathNodeLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 4, &PathNodeMeta::Instance, HOTFIX_SEL_PATH_NODE }; }; +struct PathPropertyLoadInfo +{ + static constexpr DB2FieldMeta Fields[4] = + { + { false, FT_INT, "ID" }, + { false, FT_SHORT, "PathID" }, + { false, FT_BYTE, "PropertyIndex" }, + { true, FT_INT, "Value" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 4, &PathPropertyMeta::Instance, HOTFIX_SEL_PATH_PROPERTY }; +}; + struct PhaseLoadInfo { static constexpr DB2FieldMeta Fields[2] = diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 1aef0503591..5d4090f0cf8 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -248,6 +248,7 @@ DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore("Overrid DB2Storage<ParagonReputationEntry> sParagonReputationStore("ParagonReputation.db2", &ParagonReputationLoadInfo::Instance); DB2Storage<PathEntry> sPathStore("Path.db2", &PathLoadInfo::Instance); DB2Storage<PathNodeEntry> sPathNodeStore("PathNode.db2", &PathNodeLoadInfo::Instance); +DB2Storage<PathPropertyEntry> sPathPropertyStore("PathProperty.db2", &PathPropertyLoadInfo::Instance); DB2Storage<PhaseEntry> sPhaseStore("Phase.db2", &PhaseLoadInfo::Instance); DB2Storage<PhaseXPhaseGroupEntry> sPhaseXPhaseGroupStore("PhaseXPhaseGroup.db2", &PhaseXPhaseGroupLoadInfo::Instance); DB2Storage<PlayerConditionEntry> sPlayerConditionStore("PlayerCondition.db2", &PlayerConditionLoadInfo::Instance); @@ -414,7 +415,6 @@ typedef std::unordered_map<uint32, DB2Manager::MountTypeXCapabilitySet> MountCap typedef std::unordered_map<uint32, DB2Manager::MountXDisplayContainer> MountDisplaysCointainer; typedef std::unordered_map<uint32, std::array<std::vector<NameGenEntry const*>, 2>> NameGenContainer; typedef std::array<std::vector<Trinity::wregex>, TOTAL_LOCALES + 1> NameValidationRegexContainer; -typedef std::unordered_map<uint32 /*pathID*/, std::vector<DBCPosition3D>> PathNodesContainer; typedef std::unordered_map<uint32, std::vector<uint32>> PhaseGroupContainer; typedef std::array<PowerTypeEntry const*, MAX_POWERS> PowerTypesContainer; typedef std::unordered_map<uint32, std::pair<std::vector<QuestPackageItemEntry const*>, std::vector<QuestPackageItemEntry const*>>> QuestPackageItemContainer; @@ -496,7 +496,7 @@ namespace NameGenContainer _nameGenData; NameValidationRegexContainer _nameValidators; std::unordered_map<uint32, ParagonReputationEntry const*> _paragonReputations; - PathNodesContainer _pathNodes; + std::unordered_map<uint32 /*pathID*/, PathDb2> _paths; PhaseGroupContainer _phasesByGroup; PowerTypesContainer _powerTypes; std::unordered_map<uint32, uint8> _pvpItemBonus; @@ -857,6 +857,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sParagonReputationStore); LOAD_DB2(sPathStore); LOAD_DB2(sPathNodeStore); + LOAD_DB2(sPathPropertyStore); LOAD_DB2(sPhaseStore); LOAD_DB2(sPhaseXPhaseGroupStore); LOAD_DB2(sPlayerConditionStore); @@ -1407,21 +1408,24 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul { std::unordered_map<uint32 /*pathID*/, std::vector<PathNodeEntry const*>> unsortedNodes; for (PathNodeEntry const* pathNode : sPathNodeStore) - if (sPathStore.LookupEntry(pathNode->PathID)) - if (sLocationStore.LookupEntry(pathNode->LocationID)) - unsortedNodes[pathNode->PathID].push_back(pathNode); + if (sPathStore.HasRecord(pathNode->PathID) && sLocationStore.HasRecord(pathNode->LocationID)) + unsortedNodes[pathNode->PathID].push_back(pathNode); - for (auto& [pathId, pathNodes] : unsortedNodes) + for (auto&& [pathId, pathNodes] : unsortedNodes) { - std::sort(pathNodes.begin(), pathNodes.end(), [](PathNodeEntry const* node1, PathNodeEntry const* node2) { return node1->Sequence < node2->Sequence; }); - std::vector<DBCPosition3D>& nodes = _pathNodes[pathId]; - nodes.resize(pathNodes.size()); - std::transform(pathNodes.begin(), pathNodes.end(), nodes.begin(), [](PathNodeEntry const* node) + PathDb2& path = _paths[pathId]; + + path.Locations.resize(pathNodes.size()); + std::ranges::sort(pathNodes, std::ranges::less(), &PathNodeEntry::Sequence); + std::ranges::transform(pathNodes, path.Locations.begin(), [](PathNodeEntry const* node) { - LocationEntry const* location = sLocationStore.AssertEntry(node->LocationID); - return location->Pos; + return sLocationStore.AssertEntry(node->LocationID)->Pos; }); } + + for (PathPropertyEntry const* pathProperty : sPathPropertyStore) + if (sPathStore.HasRecord(pathProperty->PathID)) + _paths[pathProperty->PathID].Properties.push_back(pathProperty); } for (PhaseXPhaseGroupEntry const* group : sPhaseXPhaseGroupStore) @@ -2815,9 +2819,9 @@ ParagonReputationEntry const* DB2Manager::GetParagonReputation(uint32 factionId) return Trinity::Containers::MapGetValuePtr(_paragonReputations, factionId); } -std::vector<DBCPosition3D> const* DB2Manager::GetNodesForPath(uint32 pathId) const +PathDb2 const* DB2Manager::GetPath(uint32 pathId) const { - return Trinity::Containers::MapGetValuePtr(_pathNodes, pathId); + return Trinity::Containers::MapGetValuePtr(_paths, pathId); } PVPDifficultyEntry const* DB2Manager::GetBattlegroundBracketByLevel(uint32 mapid, uint32 level) diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index a974d09f5e2..9a92f3f93fb 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -195,8 +195,6 @@ TC_GAME_API extern DB2Storage<MovieEntry> sMovieStore; TC_GAME_API extern DB2Storage<MythicPlusSeasonEntry> sMythicPlusSeasonStore; TC_GAME_API extern DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore; TC_GAME_API extern DB2Storage<ParagonReputationEntry> sParagonReputationStore; -TC_GAME_API extern DB2Storage<PathEntry> sPathStore; -TC_GAME_API extern DB2Storage<PathNodeEntry> sPathNodeStore; TC_GAME_API extern DB2Storage<PhaseEntry> sPhaseStore; TC_GAME_API extern DB2Storage<PlayerConditionEntry> sPlayerConditionStore; TC_GAME_API extern DB2Storage<PowerDisplayEntry> sPowerDisplayStore; @@ -318,6 +316,13 @@ struct ContentTuningLevels int16 TargetLevelMax = 0; }; +struct PathDb2 +{ + uint32 ID; + std::vector<DBCPosition3D> Locations; + std::vector<PathPropertyEntry const*> Properties; +}; + struct ShapeshiftFormModelData { uint32 OptionID; @@ -495,7 +500,7 @@ public: ResponseCodes ValidateName(std::wstring const& name, LocaleConstant locale) const; static int32 GetNumTalentsAtLevel(uint32 level, Classes playerClass); ParagonReputationEntry const* GetParagonReputation(uint32 factionId) const; - std::vector<DBCPosition3D> const* GetNodesForPath(uint32 pathId) const; + PathDb2 const* GetPath(uint32 pathId) const; std::vector<uint32> const* GetPhasesForGroup(uint32 group) const; PowerTypeEntry const* GetPowerTypeEntry(Powers power) const; PowerTypeEntry const* GetPowerTypeByName(std::string const& name) const; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 73383131db4..54de72ea26e 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -3004,6 +3004,16 @@ struct PathNodeEntry int32 LocationID; }; +struct PathPropertyEntry +{ + uint32 ID; + uint16 PathID; + uint8 PropertyIndex; + int32 Value; + + PathPropertyIndex GetPropertyIndex() const { return static_cast<PathPropertyIndex>(PropertyIndex); } +}; + struct PhaseEntry { uint32 ID; diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index faeab5f995d..4c088350339 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -1735,6 +1735,25 @@ enum MountFlags MOUNT_FLAG_HIDE_IF_UNKNOWN = 0x40 }; +enum class PathPropertyIndex : uint8 +{ + UseNewLiquidGenerateCode = 0, + AnimaCableId = 1, + AnimaPlayerCondition = 2, + AnimaStartTaper = 3, + AnimaEndTaper = 4, + VolumeHeight = 5, + AiPathGraphMaxStartDist = 6, + AiPathGraphMinTotalDist = 7, + AiPathGraphAreaControl = 8, + AiPathGraphAreaId = 9, + AiPathGraphWidth = 10, + AiPathDefaultFollowStyle = 11, + AiPathConstrainSteering = 12, + Phase = 13, + SteepSlopeDegrees = 14 +}; + enum class PhaseEntryFlags : int32 { ReadOnly = 0x001, diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b06f41e0619..5b277b524b8 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2039,9 +2039,12 @@ bool Player::IsInAreaTrigger(AreaTriggerEntry const* areaTrigger) const return false; break; case 3: // Polygon - if (!IsInPolygon2D(areaTriggerPos, sObjectMgr->GetVerticesForAreaTrigger(areaTrigger))) + { + AreaTriggerPolygon const* polygon = sObjectMgr->GetAreaTriggerPolygon(areaTrigger->ID); + if (!polygon || (polygon->Height && GetPositionZ() > areaTrigger->Pos.Z + *polygon->Height) || !IsInPolygon2D(areaTriggerPos, polygon->Vertices)) return false; break; + } case 4: // Cylinder if (!IsWithinVerticalCylinder(areaTriggerPos, areaTrigger->Radius, areaTrigger->BoxHeight)) return false; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index c5f5c0986cb..3d08f636d4f 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -6761,25 +6761,9 @@ Quest const* ObjectMgr::GetQuestTemplate(uint32 quest_id) const return itr != _questTemplates.end() ? itr->second.get() : nullptr; } -std::vector<Position> ObjectMgr::GetVerticesForAreaTrigger(AreaTriggerEntry const* areaTrigger) const +AreaTriggerPolygon const* ObjectMgr::GetAreaTriggerPolygon(uint32 areaTriggerId) const { - std::vector<Position> vertices; - if (areaTrigger && areaTrigger->ShapeType == 3 /* Polygon */) - { - if (std::vector<DBCPosition3D> const* pathNodes = sDB2Manager.GetNodesForPath(areaTrigger->ShapeID)) - { - vertices.resize(pathNodes->size()); - std::transform(pathNodes->cbegin(), pathNodes->cend(), vertices.begin(), [](DBCPosition3D dbcPosition) - { - return Position(dbcPosition.X, dbcPosition.Y, dbcPosition.Z); - }); - } - - // Drop first node (areatrigger position) - vertices.erase(vertices.begin()); - } - - return vertices; + return Trinity::Containers::MapGetValuePtr(_areaTriggerPolygons, areaTriggerId); } void ObjectMgr::LoadGraveyardZones() @@ -7200,6 +7184,30 @@ void ObjectMgr::LoadAreaTriggerTeleports() TC_LOG_INFO("server.loading", ">> Loaded {} area trigger teleport definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadAreaTriggerPolygons() +{ + for (AreaTriggerEntry const* areaTrigger : sAreaTriggerStore) + { + if (areaTrigger->ShapeType != 3) + continue; + + PathDb2 const* path = sDB2Manager.GetPath(areaTrigger->ShapeID); + if (!path || path->Locations.size() < 4) + continue; + + AreaTriggerPolygon& polygon = _areaTriggerPolygons[areaTrigger->ID]; + polygon.Vertices.resize(path->Locations.size() - 1); + std::ranges::transform(path->Locations.begin() + 1, path->Locations.end(), polygon.Vertices.begin(), [](DBCPosition3D const& pos) + { + return Position(pos.X, pos.Y, pos.Z); + }); + + for (PathPropertyEntry const* pathProperty : path->Properties) + if (pathProperty->GetPropertyIndex() == PathPropertyIndex::VolumeHeight) + polygon.Height = pathProperty->Value * 0.001f + 0.02f; + } +} + void ObjectMgr::LoadAccessRequirements() { uint32 oldMSTime = getMSTime(); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 416bbc7b141..f480829e880 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -464,6 +464,12 @@ struct AreaTriggerStruct float target_Orientation; }; +struct AreaTriggerPolygon +{ + std::vector<Position> Vertices; + Optional<float> Height; +}; + struct AccessRequirement { uint8 levelMin; @@ -1197,7 +1203,7 @@ class TC_GAME_API ObjectMgr return nullptr; } - std::vector<Position> GetVerticesForAreaTrigger(AreaTriggerEntry const* areaTrigger) const; + AreaTriggerPolygon const* GetAreaTriggerPolygon(uint32 areaTriggerId) const; bool IsTavernAreaTrigger(uint32 Trigger_ID) const { @@ -1362,6 +1368,7 @@ class TC_GAME_API ObjectMgr void LoadNPCText(); void LoadAreaTriggerTeleports(); + void LoadAreaTriggerPolygons(); void LoadAccessRequirements(); void LoadQuestAreaTriggers(); void LoadQuestGreetings(); @@ -1795,6 +1802,7 @@ class TC_GAME_API ObjectMgr QuestGreetingLocaleContainer _questGreetingLocaleStore; AreaTriggerContainer _areaTriggerStore; AreaTriggerScriptContainer _areaTriggerScriptStore; + std::unordered_map<uint32, AreaTriggerPolygon> _areaTriggerPolygons; AccessRequirementContainer _accessRequirementStore; std::unordered_map<uint32, WorldSafeLocsEntry> _worldSafeLocs; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 3912eb2ae00..7e6512fab8c 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -493,7 +493,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge return; } - if (packet.Entered && !player->IsInAreaTrigger(atEntry)) + if (packet.Entered != player->IsInAreaTrigger(atEntry)) { TC_LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player '{}' {} too far, ignore Area Trigger ID: {}", player->GetName(), player->GetGUID().ToString(), packet.AreaTriggerID); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 05b92b9d334..cb144ecb4f2 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2126,6 +2126,9 @@ bool World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Area Trigger Teleports definitions..."); sObjectMgr->LoadAreaTriggerTeleports(); + TC_LOG_INFO("server.loading", "Loading Area Trigger Polygon data..."); + sObjectMgr->LoadAreaTriggerPolygons(); + TC_LOG_INFO("server.loading", "Loading Access Requirements..."); sObjectMgr->LoadAccessRequirements(); // must be after item template load |