aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2024-05-26 22:29:57 +0200
committerShauren <shauren.trinity@gmail.com>2024-05-27 18:53:47 +0200
commit1c3268155d0165e150d239c3a808d5a8dddeae18 (patch)
tree764d238d71e7349d6001edd21dec25cefaf7d4a6 /src
parentb070e63fa867f7f25e73e9ef3aafbe18902a50e9 (diff)
Core/AreaTriggers: Implement height check for polygon db2 areatriggers
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp4
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h3
-rw-r--r--src/server/game/DataStores/DB2LoadInfo.h13
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp32
-rw-r--r--src/server/game/DataStores/DB2Stores.h11
-rw-r--r--src/server/game/DataStores/DB2Structure.h10
-rw-r--r--src/server/game/DataStores/DBCEnums.h19
-rw-r--r--src/server/game/Entities/Player/Player.cpp5
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp44
-rw-r--r--src/server/game/Globals/ObjectMgr.h10
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp2
-rw-r--r--src/server/game/World/World.cpp3
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