Skip to content

Commit 1c32681

Browse files
committed
Core/AreaTriggers: Implement height check for polygon db2 areatriggers
1 parent b070e63 commit 1c32681

File tree

13 files changed

+131
-38
lines changed

13 files changed

+131
-38
lines changed

Diff for: sql/updates/hotfixes/master/2024_05_27_00_hotfixes.sql

+13
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,16 @@ CREATE TABLE `path_node` (
4343
`VerifiedBuild` int NOT NULL DEFAULT '0',
4444
PRIMARY KEY (`ID`,`VerifiedBuild`)
4545
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
46+
47+
--
48+
-- Table structure for table `path_property`
49+
--
50+
DROP TABLE IF EXISTS `path_property`;
51+
CREATE TABLE `path_property` (
52+
`ID` int unsigned NOT NULL DEFAULT '0',
53+
`PathID` smallint unsigned NOT NULL DEFAULT '0',
54+
`PropertyIndex` tinyint unsigned NOT NULL DEFAULT '0',
55+
`Value` int NOT NULL DEFAULT '0',
56+
`VerifiedBuild` int NOT NULL DEFAULT '0',
57+
PRIMARY KEY (`ID`,`VerifiedBuild`)
58+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Diff for: src/server/database/Database/Implementation/HotfixDatabase.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,10 @@ void HotfixDatabaseConnection::DoPrepareStatements()
12351235
PrepareStatement(HOTFIX_SEL_PATH_NODE, "SELECT ID, PathID, Sequence, LocationID FROM path_node WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
12361236
PREPARE_MAX_ID_STMT(HOTFIX_SEL_PATH_NODE, "SELECT MAX(ID) + 1 FROM path_node", CONNECTION_SYNCH);
12371237

1238+
// PathProperty.db2
1239+
PrepareStatement(HOTFIX_SEL_PATH_PROPERTY, "SELECT ID, PathID, PropertyIndex, Value FROM path_property WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
1240+
PREPARE_MAX_ID_STMT(HOTFIX_SEL_PATH_PROPERTY, "SELECT MAX(ID) + 1 FROM path_property", CONNECTION_SYNCH);
1241+
12381242
// Phase.db2
12391243
PrepareStatement(HOTFIX_SEL_PHASE, "SELECT ID, Flags FROM phase WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
12401244
PREPARE_MAX_ID_STMT(HOTFIX_SEL_PHASE, "SELECT MAX(ID) + 1 FROM phase", CONNECTION_SYNCH);

Diff for: src/server/database/Database/Implementation/HotfixDatabase.h

+3
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,9 @@ enum HotfixDatabaseStatements : uint32
715715
HOTFIX_SEL_PATH_NODE,
716716
HOTFIX_SEL_PATH_NODE_MAX_ID,
717717

718+
HOTFIX_SEL_PATH_PROPERTY,
719+
HOTFIX_SEL_PATH_PROPERTY_MAX_ID,
720+
718721
HOTFIX_SEL_PHASE,
719722
HOTFIX_SEL_PHASE_MAX_ID,
720723

Diff for: src/server/game/DataStores/DB2LoadInfo.h

+13
Original file line numberDiff line numberDiff line change
@@ -4010,6 +4010,19 @@ struct PathNodeLoadInfo
40104010
static constexpr DB2LoadInfo Instance{ Fields, 4, &PathNodeMeta::Instance, HOTFIX_SEL_PATH_NODE };
40114011
};
40124012

4013+
struct PathPropertyLoadInfo
4014+
{
4015+
static constexpr DB2FieldMeta Fields[4] =
4016+
{
4017+
{ false, FT_INT, "ID" },
4018+
{ false, FT_SHORT, "PathID" },
4019+
{ false, FT_BYTE, "PropertyIndex" },
4020+
{ true, FT_INT, "Value" },
4021+
};
4022+
4023+
static constexpr DB2LoadInfo Instance{ Fields, 4, &PathPropertyMeta::Instance, HOTFIX_SEL_PATH_PROPERTY };
4024+
};
4025+
40134026
struct PhaseLoadInfo
40144027
{
40154028
static constexpr DB2FieldMeta Fields[2] =

Diff for: src/server/game/DataStores/DB2Stores.cpp

+18-14
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore("Overrid
248248
DB2Storage<ParagonReputationEntry> sParagonReputationStore("ParagonReputation.db2", &ParagonReputationLoadInfo::Instance);
249249
DB2Storage<PathEntry> sPathStore("Path.db2", &PathLoadInfo::Instance);
250250
DB2Storage<PathNodeEntry> sPathNodeStore("PathNode.db2", &PathNodeLoadInfo::Instance);
251+
DB2Storage<PathPropertyEntry> sPathPropertyStore("PathProperty.db2", &PathPropertyLoadInfo::Instance);
251252
DB2Storage<PhaseEntry> sPhaseStore("Phase.db2", &PhaseLoadInfo::Instance);
252253
DB2Storage<PhaseXPhaseGroupEntry> sPhaseXPhaseGroupStore("PhaseXPhaseGroup.db2", &PhaseXPhaseGroupLoadInfo::Instance);
253254
DB2Storage<PlayerConditionEntry> sPlayerConditionStore("PlayerCondition.db2", &PlayerConditionLoadInfo::Instance);
@@ -414,7 +415,6 @@ typedef std::unordered_map<uint32, DB2Manager::MountTypeXCapabilitySet> MountCap
414415
typedef std::unordered_map<uint32, DB2Manager::MountXDisplayContainer> MountDisplaysCointainer;
415416
typedef std::unordered_map<uint32, std::array<std::vector<NameGenEntry const*>, 2>> NameGenContainer;
416417
typedef std::array<std::vector<Trinity::wregex>, TOTAL_LOCALES + 1> NameValidationRegexContainer;
417-
typedef std::unordered_map<uint32 /*pathID*/, std::vector<DBCPosition3D>> PathNodesContainer;
418418
typedef std::unordered_map<uint32, std::vector<uint32>> PhaseGroupContainer;
419419
typedef std::array<PowerTypeEntry const*, MAX_POWERS> PowerTypesContainer;
420420
typedef std::unordered_map<uint32, std::pair<std::vector<QuestPackageItemEntry const*>, std::vector<QuestPackageItemEntry const*>>> QuestPackageItemContainer;
@@ -496,7 +496,7 @@ namespace
496496
NameGenContainer _nameGenData;
497497
NameValidationRegexContainer _nameValidators;
498498
std::unordered_map<uint32, ParagonReputationEntry const*> _paragonReputations;
499-
PathNodesContainer _pathNodes;
499+
std::unordered_map<uint32 /*pathID*/, PathDb2> _paths;
500500
PhaseGroupContainer _phasesByGroup;
501501
PowerTypesContainer _powerTypes;
502502
std::unordered_map<uint32, uint8> _pvpItemBonus;
@@ -857,6 +857,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
857857
LOAD_DB2(sParagonReputationStore);
858858
LOAD_DB2(sPathStore);
859859
LOAD_DB2(sPathNodeStore);
860+
LOAD_DB2(sPathPropertyStore);
860861
LOAD_DB2(sPhaseStore);
861862
LOAD_DB2(sPhaseXPhaseGroupStore);
862863
LOAD_DB2(sPlayerConditionStore);
@@ -1407,21 +1408,24 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
14071408
{
14081409
std::unordered_map<uint32 /*pathID*/, std::vector<PathNodeEntry const*>> unsortedNodes;
14091410
for (PathNodeEntry const* pathNode : sPathNodeStore)
1410-
if (sPathStore.LookupEntry(pathNode->PathID))
1411-
if (sLocationStore.LookupEntry(pathNode->LocationID))
1412-
unsortedNodes[pathNode->PathID].push_back(pathNode);
1411+
if (sPathStore.HasRecord(pathNode->PathID) && sLocationStore.HasRecord(pathNode->LocationID))
1412+
unsortedNodes[pathNode->PathID].push_back(pathNode);
14131413

1414-
for (auto& [pathId, pathNodes] : unsortedNodes)
1414+
for (auto&& [pathId, pathNodes] : unsortedNodes)
14151415
{
1416-
std::sort(pathNodes.begin(), pathNodes.end(), [](PathNodeEntry const* node1, PathNodeEntry const* node2) { return node1->Sequence < node2->Sequence; });
1417-
std::vector<DBCPosition3D>& nodes = _pathNodes[pathId];
1418-
nodes.resize(pathNodes.size());
1419-
std::transform(pathNodes.begin(), pathNodes.end(), nodes.begin(), [](PathNodeEntry const* node)
1416+
PathDb2& path = _paths[pathId];
1417+
1418+
path.Locations.resize(pathNodes.size());
1419+
std::ranges::sort(pathNodes, std::ranges::less(), &PathNodeEntry::Sequence);
1420+
std::ranges::transform(pathNodes, path.Locations.begin(), [](PathNodeEntry const* node)
14201421
{
1421-
LocationEntry const* location = sLocationStore.AssertEntry(node->LocationID);
1422-
return location->Pos;
1422+
return sLocationStore.AssertEntry(node->LocationID)->Pos;
14231423
});
14241424
}
1425+
1426+
for (PathPropertyEntry const* pathProperty : sPathPropertyStore)
1427+
if (sPathStore.HasRecord(pathProperty->PathID))
1428+
_paths[pathProperty->PathID].Properties.push_back(pathProperty);
14251429
}
14261430

14271431
for (PhaseXPhaseGroupEntry const* group : sPhaseXPhaseGroupStore)
@@ -2815,9 +2819,9 @@ ParagonReputationEntry const* DB2Manager::GetParagonReputation(uint32 factionId)
28152819
return Trinity::Containers::MapGetValuePtr(_paragonReputations, factionId);
28162820
}
28172821

2818-
std::vector<DBCPosition3D> const* DB2Manager::GetNodesForPath(uint32 pathId) const
2822+
PathDb2 const* DB2Manager::GetPath(uint32 pathId) const
28192823
{
2820-
return Trinity::Containers::MapGetValuePtr(_pathNodes, pathId);
2824+
return Trinity::Containers::MapGetValuePtr(_paths, pathId);
28212825
}
28222826

28232827
PVPDifficultyEntry const* DB2Manager::GetBattlegroundBracketByLevel(uint32 mapid, uint32 level)

Diff for: src/server/game/DataStores/DB2Stores.h

+8-3
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,6 @@ TC_GAME_API extern DB2Storage<MovieEntry> sMovieStore;
195195
TC_GAME_API extern DB2Storage<MythicPlusSeasonEntry> sMythicPlusSeasonStore;
196196
TC_GAME_API extern DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore;
197197
TC_GAME_API extern DB2Storage<ParagonReputationEntry> sParagonReputationStore;
198-
TC_GAME_API extern DB2Storage<PathEntry> sPathStore;
199-
TC_GAME_API extern DB2Storage<PathNodeEntry> sPathNodeStore;
200198
TC_GAME_API extern DB2Storage<PhaseEntry> sPhaseStore;
201199
TC_GAME_API extern DB2Storage<PlayerConditionEntry> sPlayerConditionStore;
202200
TC_GAME_API extern DB2Storage<PowerDisplayEntry> sPowerDisplayStore;
@@ -318,6 +316,13 @@ struct ContentTuningLevels
318316
int16 TargetLevelMax = 0;
319317
};
320318

319+
struct PathDb2
320+
{
321+
uint32 ID;
322+
std::vector<DBCPosition3D> Locations;
323+
std::vector<PathPropertyEntry const*> Properties;
324+
};
325+
321326
struct ShapeshiftFormModelData
322327
{
323328
uint32 OptionID;
@@ -495,7 +500,7 @@ class TC_GAME_API DB2Manager
495500
ResponseCodes ValidateName(std::wstring const& name, LocaleConstant locale) const;
496501
static int32 GetNumTalentsAtLevel(uint32 level, Classes playerClass);
497502
ParagonReputationEntry const* GetParagonReputation(uint32 factionId) const;
498-
std::vector<DBCPosition3D> const* GetNodesForPath(uint32 pathId) const;
503+
PathDb2 const* GetPath(uint32 pathId) const;
499504
std::vector<uint32> const* GetPhasesForGroup(uint32 group) const;
500505
PowerTypeEntry const* GetPowerTypeEntry(Powers power) const;
501506
PowerTypeEntry const* GetPowerTypeByName(std::string const& name) const;

Diff for: src/server/game/DataStores/DB2Structure.h

+10
Original file line numberDiff line numberDiff line change
@@ -3004,6 +3004,16 @@ struct PathNodeEntry
30043004
int32 LocationID;
30053005
};
30063006

3007+
struct PathPropertyEntry
3008+
{
3009+
uint32 ID;
3010+
uint16 PathID;
3011+
uint8 PropertyIndex;
3012+
int32 Value;
3013+
3014+
PathPropertyIndex GetPropertyIndex() const { return static_cast<PathPropertyIndex>(PropertyIndex); }
3015+
};
3016+
30073017
struct PhaseEntry
30083018
{
30093019
uint32 ID;

Diff for: src/server/game/DataStores/DBCEnums.h

+19
Original file line numberDiff line numberDiff line change
@@ -1735,6 +1735,25 @@ enum MountFlags
17351735
MOUNT_FLAG_HIDE_IF_UNKNOWN = 0x40
17361736
};
17371737

1738+
enum class PathPropertyIndex : uint8
1739+
{
1740+
UseNewLiquidGenerateCode = 0,
1741+
AnimaCableId = 1,
1742+
AnimaPlayerCondition = 2,
1743+
AnimaStartTaper = 3,
1744+
AnimaEndTaper = 4,
1745+
VolumeHeight = 5,
1746+
AiPathGraphMaxStartDist = 6,
1747+
AiPathGraphMinTotalDist = 7,
1748+
AiPathGraphAreaControl = 8,
1749+
AiPathGraphAreaId = 9,
1750+
AiPathGraphWidth = 10,
1751+
AiPathDefaultFollowStyle = 11,
1752+
AiPathConstrainSteering = 12,
1753+
Phase = 13,
1754+
SteepSlopeDegrees = 14
1755+
};
1756+
17381757
enum class PhaseEntryFlags : int32
17391758
{
17401759
ReadOnly = 0x001,

Diff for: src/server/game/Entities/Player/Player.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -2039,9 +2039,12 @@ bool Player::IsInAreaTrigger(AreaTriggerEntry const* areaTrigger) const
20392039
return false;
20402040
break;
20412041
case 3: // Polygon
2042-
if (!IsInPolygon2D(areaTriggerPos, sObjectMgr->GetVerticesForAreaTrigger(areaTrigger)))
2042+
{
2043+
AreaTriggerPolygon const* polygon = sObjectMgr->GetAreaTriggerPolygon(areaTrigger->ID);
2044+
if (!polygon || (polygon->Height && GetPositionZ() > areaTrigger->Pos.Z + *polygon->Height) || !IsInPolygon2D(areaTriggerPos, polygon->Vertices))
20432045
return false;
20442046
break;
2047+
}
20452048
case 4: // Cylinder
20462049
if (!IsWithinVerticalCylinder(areaTriggerPos, areaTrigger->Radius, areaTrigger->BoxHeight))
20472050
return false;

Diff for: src/server/game/Globals/ObjectMgr.cpp

+26-18
Original file line numberDiff line numberDiff line change
@@ -6761,25 +6761,9 @@ Quest const* ObjectMgr::GetQuestTemplate(uint32 quest_id) const
67616761
return itr != _questTemplates.end() ? itr->second.get() : nullptr;
67626762
}
67636763

6764-
std::vector<Position> ObjectMgr::GetVerticesForAreaTrigger(AreaTriggerEntry const* areaTrigger) const
6764+
AreaTriggerPolygon const* ObjectMgr::GetAreaTriggerPolygon(uint32 areaTriggerId) const
67656765
{
6766-
std::vector<Position> vertices;
6767-
if (areaTrigger && areaTrigger->ShapeType == 3 /* Polygon */)
6768-
{
6769-
if (std::vector<DBCPosition3D> const* pathNodes = sDB2Manager.GetNodesForPath(areaTrigger->ShapeID))
6770-
{
6771-
vertices.resize(pathNodes->size());
6772-
std::transform(pathNodes->cbegin(), pathNodes->cend(), vertices.begin(), [](DBCPosition3D dbcPosition)
6773-
{
6774-
return Position(dbcPosition.X, dbcPosition.Y, dbcPosition.Z);
6775-
});
6776-
}
6777-
6778-
// Drop first node (areatrigger position)
6779-
vertices.erase(vertices.begin());
6780-
}
6781-
6782-
return vertices;
6766+
return Trinity::Containers::MapGetValuePtr(_areaTriggerPolygons, areaTriggerId);
67836767
}
67846768

67856769
void ObjectMgr::LoadGraveyardZones()
@@ -7200,6 +7184,30 @@ void ObjectMgr::LoadAreaTriggerTeleports()
72007184
TC_LOG_INFO("server.loading", ">> Loaded {} area trigger teleport definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
72017185
}
72027186

7187+
void ObjectMgr::LoadAreaTriggerPolygons()
7188+
{
7189+
for (AreaTriggerEntry const* areaTrigger : sAreaTriggerStore)
7190+
{
7191+
if (areaTrigger->ShapeType != 3)
7192+
continue;
7193+
7194+
PathDb2 const* path = sDB2Manager.GetPath(areaTrigger->ShapeID);
7195+
if (!path || path->Locations.size() < 4)
7196+
continue;
7197+
7198+
AreaTriggerPolygon& polygon = _areaTriggerPolygons[areaTrigger->ID];
7199+
polygon.Vertices.resize(path->Locations.size() - 1);
7200+
std::ranges::transform(path->Locations.begin() + 1, path->Locations.end(), polygon.Vertices.begin(), [](DBCPosition3D const& pos)
7201+
{
7202+
return Position(pos.X, pos.Y, pos.Z);
7203+
});
7204+
7205+
for (PathPropertyEntry const* pathProperty : path->Properties)
7206+
if (pathProperty->GetPropertyIndex() == PathPropertyIndex::VolumeHeight)
7207+
polygon.Height = pathProperty->Value * 0.001f + 0.02f;
7208+
}
7209+
}
7210+
72037211
void ObjectMgr::LoadAccessRequirements()
72047212
{
72057213
uint32 oldMSTime = getMSTime();

Diff for: src/server/game/Globals/ObjectMgr.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,12 @@ struct AreaTriggerStruct
464464
float target_Orientation;
465465
};
466466

467+
struct AreaTriggerPolygon
468+
{
469+
std::vector<Position> Vertices;
470+
Optional<float> Height;
471+
};
472+
467473
struct AccessRequirement
468474
{
469475
uint8 levelMin;
@@ -1197,7 +1203,7 @@ class TC_GAME_API ObjectMgr
11971203
return nullptr;
11981204
}
11991205

1200-
std::vector<Position> GetVerticesForAreaTrigger(AreaTriggerEntry const* areaTrigger) const;
1206+
AreaTriggerPolygon const* GetAreaTriggerPolygon(uint32 areaTriggerId) const;
12011207

12021208
bool IsTavernAreaTrigger(uint32 Trigger_ID) const
12031209
{
@@ -1362,6 +1368,7 @@ class TC_GAME_API ObjectMgr
13621368
void LoadNPCText();
13631369

13641370
void LoadAreaTriggerTeleports();
1371+
void LoadAreaTriggerPolygons();
13651372
void LoadAccessRequirements();
13661373
void LoadQuestAreaTriggers();
13671374
void LoadQuestGreetings();
@@ -1795,6 +1802,7 @@ class TC_GAME_API ObjectMgr
17951802
QuestGreetingLocaleContainer _questGreetingLocaleStore;
17961803
AreaTriggerContainer _areaTriggerStore;
17971804
AreaTriggerScriptContainer _areaTriggerScriptStore;
1805+
std::unordered_map<uint32, AreaTriggerPolygon> _areaTriggerPolygons;
17981806
AccessRequirementContainer _accessRequirementStore;
17991807
std::unordered_map<uint32, WorldSafeLocsEntry> _worldSafeLocs;
18001808

Diff for: src/server/game/Handlers/MiscHandler.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge
493493
return;
494494
}
495495

496-
if (packet.Entered && !player->IsInAreaTrigger(atEntry))
496+
if (packet.Entered != player->IsInAreaTrigger(atEntry))
497497
{
498498
TC_LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player '{}' {} too far, ignore Area Trigger ID: {}",
499499
player->GetName(), player->GetGUID().ToString(), packet.AreaTriggerID);

Diff for: src/server/game/World/World.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -2126,6 +2126,9 @@ bool World::SetInitialWorldSettings()
21262126
TC_LOG_INFO("server.loading", "Loading Area Trigger Teleports definitions...");
21272127
sObjectMgr->LoadAreaTriggerTeleports();
21282128

2129+
TC_LOG_INFO("server.loading", "Loading Area Trigger Polygon data...");
2130+
sObjectMgr->LoadAreaTriggerPolygons();
2131+
21292132
TC_LOG_INFO("server.loading", "Loading Access Requirements...");
21302133
sObjectMgr->LoadAccessRequirements(); // must be after item template load
21312134

0 commit comments

Comments
 (0)