aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/hotfixes/master/2024_05_27_00_hotfixes.sql45
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp12
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h9
-rw-r--r--src/server/game/DataStores/DB2LoadInfo.h46
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp33
-rw-r--r--src/server/game/DataStores/DB2Stores.h4
-rw-r--r--src/server/game/DataStores/DB2Structure.h27
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTrigger.cpp71
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTrigger.h1
-rw-r--r--src/server/game/Entities/Object/Position.cpp101
-rw-r--r--src/server/game/Entities/Object/Position.h9
-rw-r--r--src/server/game/Entities/Player/Player.cpp41
-rw-r--r--src/server/game/Entities/Player/Player.h2
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp21
-rw-r--r--src/server/game/Globals/ObjectMgr.h2
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h6
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp6
17 files changed, 324 insertions, 112 deletions
diff --git a/sql/updates/hotfixes/master/2024_05_27_00_hotfixes.sql b/sql/updates/hotfixes/master/2024_05_27_00_hotfixes.sql
new file mode 100644
index 00000000000..dae069efc75
--- /dev/null
+++ b/sql/updates/hotfixes/master/2024_05_27_00_hotfixes.sql
@@ -0,0 +1,45 @@
+--
+-- Table structure for table `location`
+--
+DROP TABLE IF EXISTS `location`;
+CREATE TABLE `location` (
+ `ID` int unsigned NOT NULL DEFAULT '0',
+ `PosX` float NOT NULL DEFAULT '0',
+ `PosY` float NOT NULL DEFAULT '0',
+ `PosZ` float NOT NULL DEFAULT '0',
+ `Rot1` float NOT NULL DEFAULT '0',
+ `Rot2` float NOT NULL DEFAULT '0',
+ `Rot3` float NOT NULL DEFAULT '0',
+ `VerifiedBuild` int NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Table structure for table `path`
+--
+DROP TABLE IF EXISTS `path`;
+CREATE TABLE `path` (
+ `ID` int unsigned NOT NULL DEFAULT '0',
+ `Type` tinyint unsigned NOT NULL DEFAULT '0',
+ `SplineType` tinyint unsigned NOT NULL DEFAULT '0',
+ `Red` tinyint unsigned NOT NULL DEFAULT '0',
+ `Green` tinyint unsigned NOT NULL DEFAULT '0',
+ `Blue` tinyint unsigned NOT NULL DEFAULT '0',
+ `Alpha` tinyint unsigned NOT NULL DEFAULT '0',
+ `Flags` tinyint unsigned NOT NULL DEFAULT '0',
+ `VerifiedBuild` int NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Table structure for table `path_node`
+--
+DROP TABLE IF EXISTS `path_node`;
+CREATE TABLE `path_node` (
+ `ID` int unsigned NOT NULL DEFAULT '0',
+ `PathID` smallint unsigned NOT NULL DEFAULT '0',
+ `Sequence` smallint NOT NULL DEFAULT '0',
+ `LocationID` int NOT NULL DEFAULT '0',
+ `VerifiedBuild` int NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp
index 63b3ab237de..e852d0e4f25 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.cpp
+++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp
@@ -1110,6 +1110,10 @@ void HotfixDatabaseConnection::DoPrepareStatements()
"Coefficient3, Coefficient4 FROM liquid_type WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_LIQUID_TYPE, "SELECT MAX(ID) + 1 FROM liquid_type", CONNECTION_SYNCH);
+ // Location.db2
+ PrepareStatement(HOTFIX_SEL_LOCATION, "SELECT ID, PosX, PosY, PosZ, Rot1, Rot2, Rot3 FROM location WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
+ PREPARE_MAX_ID_STMT(HOTFIX_SEL_LOCATION, "SELECT MAX(ID) + 1 FROM location", CONNECTION_SYNCH);
+
// Lock.db2
PrepareStatement(HOTFIX_SEL_LOCK, "SELECT ID, Flags, Index1, Index2, Index3, Index4, Index5, Index6, Index7, Index8, Skill1, Skill2, Skill3, "
"Skill4, Skill5, Skill6, Skill7, Skill8, Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Action1, Action2, Action3, Action4, Action5, "
@@ -1223,6 +1227,14 @@ void HotfixDatabaseConnection::DoPrepareStatements()
" WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_PARAGON_REPUTATION, "SELECT MAX(ID) + 1 FROM paragon_reputation", CONNECTION_SYNCH);
+ // Path.db2
+ PrepareStatement(HOTFIX_SEL_PATH, "SELECT ID, Type, SplineType, Red, Green, Blue, Alpha, Flags FROM path WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
+ PREPARE_MAX_ID_STMT(HOTFIX_SEL_PATH, "SELECT MAX(ID) + 1 FROM path", CONNECTION_SYNCH);
+
+ // PathNode.db2
+ 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);
+
// 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 d1b3f022312..5ffe7d2560c 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.h
+++ b/src/server/database/Database/Implementation/HotfixDatabase.h
@@ -637,6 +637,9 @@ enum HotfixDatabaseStatements : uint32
HOTFIX_SEL_LIQUID_TYPE,
HOTFIX_SEL_LIQUID_TYPE_MAX_ID,
+ HOTFIX_SEL_LOCATION,
+ HOTFIX_SEL_LOCATION_MAX_ID,
+
HOTFIX_SEL_LOCK,
HOTFIX_SEL_LOCK_MAX_ID,
@@ -706,6 +709,12 @@ enum HotfixDatabaseStatements : uint32
HOTFIX_SEL_PARAGON_REPUTATION,
HOTFIX_SEL_PARAGON_REPUTATION_MAX_ID,
+ HOTFIX_SEL_PATH,
+ HOTFIX_SEL_PATH_MAX_ID,
+
+ HOTFIX_SEL_PATH_NODE,
+ HOTFIX_SEL_PATH_NODE_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 db8879fca37..0040ad734cf 100644
--- a/src/server/game/DataStores/DB2LoadInfo.h
+++ b/src/server/game/DataStores/DB2LoadInfo.h
@@ -3601,6 +3601,22 @@ struct LiquidTypeLoadInfo
static constexpr DB2LoadInfo Instance{ Fields, 56, &LiquidTypeMeta::Instance, HOTFIX_SEL_LIQUID_TYPE };
};
+struct LocationLoadInfo
+{
+ static constexpr DB2FieldMeta Fields[7] =
+ {
+ { false, FT_INT, "ID" },
+ { false, FT_FLOAT, "PosX" },
+ { false, FT_FLOAT, "PosY" },
+ { false, FT_FLOAT, "PosZ" },
+ { false, FT_FLOAT, "Rot1" },
+ { false, FT_FLOAT, "Rot2" },
+ { false, FT_FLOAT, "Rot3" },
+ };
+
+ static constexpr DB2LoadInfo Instance{ Fields, 7, &LocationMeta::Instance, HOTFIX_SEL_LOCATION };
+};
+
struct LockLoadInfo
{
static constexpr DB2FieldMeta Fields[34] =
@@ -3964,6 +3980,36 @@ struct ParagonReputationLoadInfo
static constexpr DB2LoadInfo Instance{ Fields, 4, &ParagonReputationMeta::Instance, HOTFIX_SEL_PARAGON_REPUTATION };
};
+struct PathLoadInfo
+{
+ static constexpr DB2FieldMeta Fields[8] =
+ {
+ { false, FT_INT, "ID" },
+ { false, FT_BYTE, "Type" },
+ { false, FT_BYTE, "SplineType" },
+ { false, FT_BYTE, "Red" },
+ { false, FT_BYTE, "Green" },
+ { false, FT_BYTE, "Blue" },
+ { false, FT_BYTE, "Alpha" },
+ { false, FT_BYTE, "Flags" },
+ };
+
+ static constexpr DB2LoadInfo Instance{ Fields, 8, &PathMeta::Instance, HOTFIX_SEL_PATH };
+};
+
+struct PathNodeLoadInfo
+{
+ static constexpr DB2FieldMeta Fields[4] =
+ {
+ { false, FT_INT, "ID" },
+ { false, FT_SHORT, "PathID" },
+ { true, FT_SHORT, "Sequence" },
+ { true, FT_INT, "LocationID" },
+ };
+
+ static constexpr DB2LoadInfo Instance{ Fields, 4, &PathNodeMeta::Instance, HOTFIX_SEL_PATH_NODE };
+};
+
struct PhaseLoadInfo
{
static constexpr DB2FieldMeta Fields[2] =
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp
index 3ab9d586b63..1aef0503591 100644
--- a/src/server/game/DataStores/DB2Stores.cpp
+++ b/src/server/game/DataStores/DB2Stores.cpp
@@ -224,6 +224,7 @@ DB2Storage<LanguagesEntry> sLanguagesStore("Languages.db2",
DB2Storage<LFGDungeonsEntry> sLFGDungeonsStore("LFGDungeons.db2", &LfgDungeonsLoadInfo::Instance);
DB2Storage<LightEntry> sLightStore("Light.db2", &LightLoadInfo::Instance);
DB2Storage<LiquidTypeEntry> sLiquidTypeStore("LiquidType.db2", &LiquidTypeLoadInfo::Instance);
+DB2Storage<LocationEntry> sLocationStore("Location.db2", &LocationLoadInfo::Instance);
DB2Storage<LockEntry> sLockStore("Lock.db2", &LockLoadInfo::Instance);
DB2Storage<MailTemplateEntry> sMailTemplateStore("MailTemplate.db2", &MailTemplateLoadInfo::Instance);
DB2Storage<MapEntry> sMapStore("Map.db2", &MapLoadInfo::Instance);
@@ -245,6 +246,8 @@ DB2Storage<NamesReservedLocaleEntry> sNamesReservedLocaleStore("Names
DB2Storage<NumTalentsAtLevelEntry> sNumTalentsAtLevelStore("NumTalentsAtLevel.db2", &NumTalentsAtLevelLoadInfo::Instance);
DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore("OverrideSpellData.db2", &OverrideSpellDataLoadInfo::Instance);
DB2Storage<ParagonReputationEntry> sParagonReputationStore("ParagonReputation.db2", &ParagonReputationLoadInfo::Instance);
+DB2Storage<PathEntry> sPathStore("Path.db2", &PathLoadInfo::Instance);
+DB2Storage<PathNodeEntry> sPathNodeStore("PathNode.db2", &PathNodeLoadInfo::Instance);
DB2Storage<PhaseEntry> sPhaseStore("Phase.db2", &PhaseLoadInfo::Instance);
DB2Storage<PhaseXPhaseGroupEntry> sPhaseXPhaseGroupStore("PhaseXPhaseGroup.db2", &PhaseXPhaseGroupLoadInfo::Instance);
DB2Storage<PlayerConditionEntry> sPlayerConditionStore("PlayerCondition.db2", &PlayerConditionLoadInfo::Instance);
@@ -411,6 +414,7 @@ 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;
@@ -492,6 +496,7 @@ namespace
NameGenContainer _nameGenData;
NameValidationRegexContainer _nameValidators;
std::unordered_map<uint32, ParagonReputationEntry const*> _paragonReputations;
+ PathNodesContainer _pathNodes;
PhaseGroupContainer _phasesByGroup;
PowerTypesContainer _powerTypes;
std::unordered_map<uint32, uint8> _pvpItemBonus;
@@ -828,6 +833,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
LOAD_DB2(sLFGDungeonsStore);
LOAD_DB2(sLightStore);
LOAD_DB2(sLiquidTypeStore);
+ LOAD_DB2(sLocationStore);
LOAD_DB2(sLockStore);
LOAD_DB2(sMailTemplateStore);
LOAD_DB2(sMapStore);
@@ -849,6 +855,8 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
LOAD_DB2(sNumTalentsAtLevelStore);
LOAD_DB2(sOverrideSpellDataStore);
LOAD_DB2(sParagonReputationStore);
+ LOAD_DB2(sPathStore);
+ LOAD_DB2(sPathNodeStore);
LOAD_DB2(sPhaseStore);
LOAD_DB2(sPhaseXPhaseGroupStore);
LOAD_DB2(sPlayerConditionStore);
@@ -1396,6 +1404,26 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
if (sFactionStore.HasRecord(paragonReputation->FactionID))
_paragonReputations[paragonReputation->FactionID] = paragonReputation;
+ {
+ 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);
+
+ 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)
+ {
+ LocationEntry const* location = sLocationStore.AssertEntry(node->LocationID);
+ return location->Pos;
+ });
+ }
+ }
+
for (PhaseXPhaseGroupEntry const* group : sPhaseXPhaseGroupStore)
if (PhaseEntry const* phase = sPhaseStore.LookupEntry(group->PhaseID))
_phasesByGroup[group->PhaseGroupID].push_back(phase->ID);
@@ -2787,6 +2815,11 @@ ParagonReputationEntry const* DB2Manager::GetParagonReputation(uint32 factionId)
return Trinity::Containers::MapGetValuePtr(_paragonReputations, factionId);
}
+std::vector<DBCPosition3D> const* DB2Manager::GetNodesForPath(uint32 pathId) const
+{
+ return Trinity::Containers::MapGetValuePtr(_pathNodes, pathId);
+}
+
PVPDifficultyEntry const* DB2Manager::GetBattlegroundBracketByLevel(uint32 mapid, uint32 level)
{
PVPDifficultyEntry const* maxEntry = nullptr; // used for level > max listed level case
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index 4f3c9676c8d..a974d09f5e2 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -181,6 +181,7 @@ TC_GAME_API extern DB2Storage<LanguagesEntry> sLanguagesSt
TC_GAME_API extern DB2Storage<LFGDungeonsEntry> sLFGDungeonsStore;
TC_GAME_API extern DB2Storage<LightEntry> sLightStore;
TC_GAME_API extern DB2Storage<LiquidTypeEntry> sLiquidTypeStore;
+TC_GAME_API extern DB2Storage<LocationEntry> sLocationStore;
TC_GAME_API extern DB2Storage<LockEntry> sLockStore;
TC_GAME_API extern DB2Storage<MailTemplateEntry> sMailTemplateStore;
TC_GAME_API extern DB2Storage<MapEntry> sMapStore;
@@ -194,6 +195,8 @@ 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;
@@ -492,6 +495,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;
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 6f60ab39414..73383131db4 100644
--- a/src/server/game/DataStores/DB2Structure.h
+++ b/src/server/game/DataStores/DB2Structure.h
@@ -2679,6 +2679,13 @@ struct LiquidTypeEntry
std::array<float, 4> Coefficient;
};
+struct LocationEntry
+{
+ uint32 ID;
+ DBCPosition3D Pos;
+ std::array<float, 3> Rot;
+};
+
#define MAX_LOCK_CASE 8
struct LockEntry
@@ -2977,6 +2984,26 @@ struct ParagonReputationEntry
int32 QuestID;
};
+struct PathEntry
+{
+ uint32 ID;
+ uint8 Type;
+ uint8 SplineType;
+ uint8 Red;
+ uint8 Green;
+ uint8 Blue;
+ uint8 Alpha;
+ uint8 Flags;
+};
+
+struct PathNodeEntry
+{
+ uint32 ID;
+ uint16 PathID;
+ int16 Sequence;
+ int32 LocationID;
+};
+
struct PhaseEntry
{
uint32 ID;
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp
index af7170cf345..85fbcc8f27a 100644
--- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp
+++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp
@@ -705,7 +705,7 @@ void AreaTrigger::SearchUnitInPolygon(std::vector<Unit*>& targetList)
{
return unit->GetPositionZ() < minZ
|| unit->GetPositionZ() > maxZ
- || !CheckIsInPolygon2D(unit);
+ || !unit->IsInPolygon2D(*this, _polygonVertices);
});
}
@@ -919,75 +919,6 @@ void AreaTrigger::UpdatePolygonVertices()
_verticesUpdatePreviousOrientation = newOrientation;
}
-bool AreaTrigger::CheckIsInPolygon2D(Position const* pos) const
-{
- float testX = pos->GetPositionX();
- float testY = pos->GetPositionY();
-
- //this method uses the ray tracing algorithm to determine if the point is in the polygon
- bool locatedInPolygon = false;
-
- for (std::size_t vertex = 0; vertex < _polygonVertices.size(); ++vertex)
- {
- std::size_t nextVertex;
-
- //repeat loop for all sets of points
- if (vertex == (_polygonVertices.size() - 1))
- {
- //if i is the last vertex, let j be the first vertex
- nextVertex = 0;
- }
- else
- {
- //for all-else, let j=(i+1)th vertex
- nextVertex = vertex + 1;
- }
-
- float vertX_i = GetPositionX() + _polygonVertices[vertex].GetPositionX();
- float vertY_i = GetPositionY() + _polygonVertices[vertex].GetPositionY();
- float vertX_j = GetPositionX() + _polygonVertices[nextVertex].GetPositionX();
- float vertY_j = GetPositionY() + _polygonVertices[nextVertex].GetPositionY();
-
- // following statement checks if testPoint.Y is below Y-coord of i-th vertex
- bool belowLowY = vertY_i > testY;
- // following statement checks if testPoint.Y is below Y-coord of i+1-th vertex
- bool belowHighY = vertY_j > testY;
-
- /* following statement is true if testPoint.Y satisfies either (only one is possible)
- -->(i).Y < testPoint.Y < (i+1).Y OR
- -->(i).Y > testPoint.Y > (i+1).Y
-
- (Note)
- Both of the conditions indicate that a point is located within the edges of the Y-th coordinate
- of the (i)-th and the (i+1)- th vertices of the polygon. If neither of the above
- conditions is satisfied, then it is assured that a semi-infinite horizontal line draw
- to the right from the testpoint will NOT cross the line that connects vertices i and i+1
- of the polygon
- */
- bool withinYsEdges = belowLowY != belowHighY;
-
- if (withinYsEdges)
- {
- // this is the slope of the line that connects vertices i and i+1 of the polygon
- float slopeOfLine = (vertX_j - vertX_i) / (vertY_j - vertY_i);
-
- // this looks up the x-coord of a point lying on the above line, given its y-coord
- float pointOnLine = (slopeOfLine* (testY - vertY_i)) + vertX_i;
-
- //checks to see if x-coord of testPoint is smaller than the point on the line with the same y-coord
- bool isLeftToLine = testX < pointOnLine;
-
- if (isLeftToLine)
- {
- //this statement changes true to false (and vice-versa)
- locatedInPolygon = !locatedInPolygon;
- }//end if (isLeftToLine)
- }//end if (withinYsEdges
- }
-
- return locatedInPolygon;
-}
-
bool AreaTrigger::HasOverridePosition() const
{
return m_areaTriggerData->OverrideMoveCurveX->OverrideActive
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.h b/src/server/game/Entities/AreaTrigger/AreaTrigger.h
index 5eaa82e1a83..7d454298108 100644
--- a/src/server/game/Entities/AreaTrigger/AreaTrigger.h
+++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.h
@@ -190,7 +190,6 @@ class TC_GAME_API AreaTrigger final : public WorldObject, public GridObject<Area
void SearchUnitInCylinder(std::vector<Unit*>& targetList);
void SearchUnitInDisk(std::vector<Unit*>& targetList);
void SearchUnitInBoundedPlane(std::vector<Unit*>& targetList);
- bool CheckIsInPolygon2D(Position const* pos) const;
void HandleUnitEnterExit(std::vector<Unit*> const& targetList);
void DoActions(Unit* unit);
diff --git a/src/server/game/Entities/Object/Position.cpp b/src/server/game/Entities/Object/Position.cpp
index d91c55eb84a..aa391a0d062 100644
--- a/src/server/game/Entities/Object/Position.cpp
+++ b/src/server/game/Entities/Object/Position.cpp
@@ -62,38 +62,109 @@ Position Position::GetPositionWithOffset(Position const& offset) const
return ret;
}
-bool Position::IsWithinBox(Position const& center, float xradius, float yradius, float zradius) const
+bool Position::IsWithinBox(Position const& boxOrigin, float length, float width, float height) const
{
// rotate the WorldObject position instead of rotating the whole cube, that way we can make a simplified
// is-in-cube check and we have to calculate only one point instead of 4
// 2PI = 360*, keep in mind that ingame orientation is counter-clockwise
- double rotation = 2 * M_PI - center.GetOrientation();
+ double rotation = 2 * M_PI - boxOrigin.GetOrientation();
double sinVal = std::sin(rotation);
double cosVal = std::cos(rotation);
- float BoxDistX = GetPositionX() - center.GetPositionX();
- float BoxDistY = GetPositionY() - center.GetPositionY();
+ float BoxDistX = GetPositionX() - boxOrigin.GetPositionX();
+ float BoxDistY = GetPositionY() - boxOrigin.GetPositionY();
- float rotX = float(center.GetPositionX() + BoxDistX * cosVal - BoxDistY*sinVal);
- float rotY = float(center.GetPositionY() + BoxDistY * cosVal + BoxDistX*sinVal);
+ float rotX = float(boxOrigin.GetPositionX() + BoxDistX * cosVal - BoxDistY * sinVal);
+ float rotY = float(boxOrigin.GetPositionY() + BoxDistY * cosVal + BoxDistX * sinVal);
// box edges are parallel to coordiante axis, so we can treat every dimension independently :D
- float dz = GetPositionZ() - center.GetPositionZ();
- float dx = rotX - center.GetPositionX();
- float dy = rotY - center.GetPositionY();
- if ((std::fabs(dx) > xradius) ||
- (std::fabs(dy) > yradius) ||
- (std::fabs(dz) > zradius))
+ float dz = GetPositionZ() - boxOrigin.GetPositionZ();
+ float dx = rotX - boxOrigin.GetPositionX();
+ float dy = rotY - boxOrigin.GetPositionY();
+ if ((std::fabs(dx) > length) ||
+ (std::fabs(dy) > width) ||
+ (std::fabs(dz) > height))
return false;
return true;
}
-bool Position::IsWithinDoubleVerticalCylinder(Position const* center, float radius, float height) const
+bool Position::IsWithinVerticalCylinder(Position const& cylinderOrigin, float radius, float height, bool isDoubleVertical) const
{
- float verticalDelta = GetPositionZ() - center->GetPositionZ();
- return IsInDist2d(center, radius) && std::abs(verticalDelta) <= height;
+ float verticalDelta = GetPositionZ() - cylinderOrigin.GetPositionZ();
+ bool isValidPositionZ = isDoubleVertical ? std::abs(verticalDelta) <= height : 0 <= verticalDelta && verticalDelta <= height;
+
+ return isValidPositionZ && IsInDist2d(cylinderOrigin, radius);
+}
+
+bool Position::IsInPolygon2D(Position const& polygonOrigin, std::span<Position const> vertices) const
+{
+ float testX = GetPositionX();
+ float testY = GetPositionY();
+
+ //this method uses the ray tracing algorithm to determine if the point is in the polygon
+ bool locatedInPolygon = false;
+
+ for (std::size_t vertex = 0; vertex < vertices.size(); ++vertex)
+ {
+ std::size_t nextVertex;
+
+ //repeat loop for all sets of points
+ if (vertex == (vertices.size() - 1))
+ {
+ //if i is the last vertex, let j be the first vertex
+ nextVertex = 0;
+ }
+ else
+ {
+ //for all-else, let j=(i+1)th vertex
+ nextVertex = vertex + 1;
+ }
+
+ float vertX_i = polygonOrigin.GetPositionX() + vertices[vertex].GetPositionX();
+ float vertY_i = polygonOrigin.GetPositionY() + vertices[vertex].GetPositionY();
+ float vertX_j = polygonOrigin.GetPositionX() + vertices[nextVertex].GetPositionX();
+ float vertY_j = polygonOrigin.GetPositionY() + vertices[nextVertex].GetPositionY();
+
+ // following statement checks if testPoint.Y is below Y-coord of i-th vertex
+ bool belowLowY = vertY_i > testY;
+ // following statement checks if testPoint.Y is below Y-coord of i+1-th vertex
+ bool belowHighY = vertY_j > testY;
+
+ /* following statement is true if testPoint.Y satisfies either (only one is possible)
+ -->(i).Y < testPoint.Y < (i+1).Y OR
+ -->(i).Y > testPoint.Y > (i+1).Y
+
+ (Note)
+ Both of the conditions indicate that a point is located within the edges of the Y-th coordinate
+ of the (i)-th and the (i+1)- th vertices of the polygon. If neither of the above
+ conditions is satisfied, then it is assured that a semi-infinite horizontal line draw
+ to the right from the testpoint will NOT cross the line that connects vertices i and i+1
+ of the polygon
+ */
+ bool withinYsEdges = belowLowY != belowHighY;
+
+ if (withinYsEdges)
+ {
+ // this is the slope of the line that connects vertices i and i+1 of the polygon
+ float slopeOfLine = (vertX_j - vertX_i) / (vertY_j - vertY_i);
+
+ // this looks up the x-coord of a point lying on the above line, given its y-coord
+ float pointOnLine = (slopeOfLine * (testY - vertY_i)) + vertX_i;
+
+ //checks to see if x-coord of testPoint is smaller than the point on the line with the same y-coord
+ bool isLeftToLine = testX < pointOnLine;
+
+ if (isLeftToLine)
+ {
+ //this statement changes true to false (and vice-versa)
+ locatedInPolygon = !locatedInPolygon;
+ }//end if (isLeftToLine)
+ }//end if (withinYsEdges
+ }
+
+ return locatedInPolygon;
}
bool Position::HasInArc(float arc, Position const* obj, float border) const
diff --git a/src/server/game/Entities/Object/Position.h b/src/server/game/Entities/Object/Position.h
index 1bd11b69c88..ed4709dadf3 100644
--- a/src/server/game/Entities/Object/Position.h
+++ b/src/server/game/Entities/Object/Position.h
@@ -19,6 +19,7 @@
#define Trinity_game_Position_h__
#include "Define.h"
+#include <span>
#include <string>
#include <cmath>
@@ -138,16 +139,18 @@ public:
float GetRelativeAngle(Position const* pos) const { return ToRelativeAngle(GetAbsoluteAngle(pos)); }
constexpr bool IsInDist2d(float x, float y, float dist) const { return GetExactDist2dSq(x, y) < dist * dist; }
+ constexpr bool IsInDist2d(Position const& pos, float dist) const { return GetExactDist2dSq(pos) < dist * dist; }
constexpr bool IsInDist2d(Position const* pos, float dist) const { return GetExactDist2dSq(pos) < dist * dist; }
constexpr bool IsInDist(float x, float y, float z, float dist) const { return GetExactDistSq(x, y, z) < dist * dist; }
constexpr bool IsInDist(Position const& pos, float dist) const { return GetExactDistSq(pos) < dist * dist; }
constexpr bool IsInDist(Position const* pos, float dist) const { return GetExactDistSq(pos) < dist * dist; }
- bool IsWithinBox(Position const& center, float xradius, float yradius, float zradius) const;
+ bool IsWithinBox(Position const& boxOrigin, float length, float width, float height) const;
- // dist2d < radius && abs(dz) < height
- bool IsWithinDoubleVerticalCylinder(Position const* center, float radius, float height) const;
+ bool IsWithinVerticalCylinder(Position const& cylinderOrigin, float radius, float height, bool isDoubleVertical = false) const;
+
+ bool IsInPolygon2D(Position const& polygonOrigin, std::span<Position const> vertices) const;
bool HasInArc(float arcangle, Position const* pos, float border = 2.0f) const;
bool HasInLine(Position const* pos, float objSize, float width) const;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index aad59f7930d..b06f41e0619 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -1029,7 +1029,7 @@ void Player::Update(uint32 p_time)
if (_restMgr->HasRestFlag(REST_FLAG_IN_TAVERN))
{
AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(_restMgr->GetInnTriggerID());
- if (!atEntry || !IsInAreaTriggerRadius(atEntry))
+ if (!atEntry || !IsInAreaTrigger(atEntry))
_restMgr->RemoveRestFlag(REST_FLAG_IN_TAVERN);
}
@@ -2015,29 +2015,38 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid, Gameo
return go;
}
-bool Player::IsInAreaTriggerRadius(AreaTriggerEntry const* trigger) const
+bool Player::IsInAreaTrigger(AreaTriggerEntry const* areaTrigger) const
{
- if (!trigger)
+ if (!areaTrigger)
return false;
- if (int32(GetMapId()) != trigger->ContinentID && !GetPhaseShift().HasVisibleMapId(trigger->ContinentID))
+ if (int32(GetMapId()) != areaTrigger->ContinentID && !GetPhaseShift().HasVisibleMapId(areaTrigger->ContinentID))
return false;
- if (trigger->PhaseID || trigger->PhaseGroupID || trigger->PhaseUseFlags)
- if (!PhasingHandler::InDbPhaseShift(this, trigger->PhaseUseFlags, trigger->PhaseID, trigger->PhaseGroupID))
+ if (areaTrigger->PhaseID || areaTrigger->PhaseGroupID || areaTrigger->PhaseUseFlags)
+ if (!PhasingHandler::InDbPhaseShift(this, areaTrigger->PhaseUseFlags, areaTrigger->PhaseID, areaTrigger->PhaseGroupID))
return false;
- if (trigger->Radius > 0.f)
+ Position areaTriggerPos(areaTrigger->Pos.X, areaTrigger->Pos.Y, areaTrigger->Pos.Z, areaTrigger->BoxYaw);
+ switch (areaTrigger->ShapeType)
{
- // if we have radius check it
- float dist = GetDistance(trigger->Pos.X, trigger->Pos.Y, trigger->Pos.Z);
- if (dist > trigger->Radius)
- return false;
- }
- else
- {
- Position center(trigger->Pos.X, trigger->Pos.Y, trigger->Pos.Z, trigger->BoxYaw);
- if (!IsWithinBox(center, trigger->BoxLength / 2.f, trigger->BoxWidth / 2.f, trigger->BoxHeight / 2.f))
+ case 0: // Sphere
+ if (!IsInDist(&areaTriggerPos, areaTrigger->Radius))
+ return false;
+ break;
+ case 1: // Box
+ if (!IsWithinBox(areaTriggerPos, areaTrigger->BoxLength / 2.f, areaTrigger->BoxWidth / 2.f, areaTrigger->BoxHeight / 2.f))
+ return false;
+ break;
+ case 3: // Polygon
+ if (!IsInPolygon2D(areaTriggerPos, sObjectMgr->GetVerticesForAreaTrigger(areaTrigger)))
+ return false;
+ break;
+ case 4: // Cylinder
+ if (!IsWithinVerticalCylinder(areaTriggerPos, areaTrigger->Radius, areaTrigger->BoxHeight))
+ return false;
+ break;
+ default:
return false;
}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 24bc8ae574b..e427a2639d4 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1178,7 +1178,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const override;
- bool IsInAreaTriggerRadius(AreaTriggerEntry const* trigger) const;
+ bool IsInAreaTrigger(AreaTriggerEntry const* areaTrigger) const;
void SendInitialPacketsBeforeAddToMap();
void SendInitialPacketsAfterAddToMap();
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 838f5f575c1..c5f5c0986cb 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -6761,6 +6761,27 @@ 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
+{
+ 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;
+}
+
void ObjectMgr::LoadGraveyardZones()
{
uint32 oldMSTime = getMSTime();
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 5e599b242d6..416bbc7b141 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -1197,6 +1197,8 @@ class TC_GAME_API ObjectMgr
return nullptr;
}
+ std::vector<Position> GetVerticesForAreaTrigger(AreaTriggerEntry const* areaTrigger) const;
+
bool IsTavernAreaTrigger(uint32 Trigger_ID) const
{
return _tavernAreaTriggerStore.find(Trigger_ID) != _tavernAreaTriggerStore.end();
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h
index bbb795be802..1e9f7801af6 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.h
@@ -1010,7 +1010,7 @@ namespace Trinity
if (i_incTargetRadius)
searchRadius += u->GetCombatReach();
- if (!u->IsInMap(i_obj) || !u->InSamePhase(i_obj) || !u->IsWithinDoubleVerticalCylinder(i_obj, searchRadius, searchRadius))
+ if (!u->IsInMap(i_obj) || !u->InSamePhase(i_obj) || !u->IsWithinVerticalCylinder(*i_obj, searchRadius, searchRadius, true))
return false;
if (!i_funit->IsFriendlyTo(u))
@@ -1059,7 +1059,7 @@ namespace Trinity
if (i_incTargetRadius)
searchRadius += u->GetCombatReach();
- return u->IsInMap(_source) && u->InSamePhase(_source) && u->IsWithinDoubleVerticalCylinder(_source, searchRadius, searchRadius);
+ return u->IsInMap(_source) && u->InSamePhase(_source) && u->IsWithinVerticalCylinder(*_source, searchRadius, searchRadius, true);
}
private:
@@ -1155,7 +1155,7 @@ namespace Trinity
if (i_incTargetRadius)
searchRadius += u->GetCombatReach();
- return u->IsInMap(i_obj) && u->InSamePhase(i_obj) && u->IsWithinDoubleVerticalCylinder(i_obj, searchRadius, searchRadius);
+ return u->IsInMap(i_obj) && u->InSamePhase(i_obj) && u->IsWithinVerticalCylinder(*i_obj, searchRadius, searchRadius, true);
}
private:
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 4d1afd9ded2..3912eb2ae00 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -480,7 +480,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge
Player* player = GetPlayer();
if (player->IsInFlight())
{
- TC_LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player '{}' {} in flight, ignore Area Trigger ID:{}",
+ TC_LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player '{}' {} in flight, ignore Area Trigger ID: {}",
player->GetName(), player->GetGUID().ToString(), packet.AreaTriggerID);
return;
}
@@ -488,12 +488,12 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge
AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(packet.AreaTriggerID);
if (!atEntry)
{
- TC_LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player '{}' {} send unknown (by DBC) Area Trigger ID:{}",
+ TC_LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player '{}' {} send unknown (by DBC) Area Trigger ID: {}",
player->GetName(), player->GetGUID().ToString(), packet.AreaTriggerID);
return;
}
- if (packet.Entered && !player->IsInAreaTriggerRadius(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);