diff options
author | Meji <alvaro.megias@outlook.com> | 2024-05-04 13:20:13 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2024-05-27 18:53:47 +0200 |
commit | b070e63fa867f7f25e73e9ef3aafbe18902a50e9 (patch) | |
tree | c3d0151e9b46d30ccb69d57983df431acfb673a3 /src | |
parent | 38e99e1569fcba821211fcd2d7c3a0b2bd00701c (diff) |
Core/AreaTriggers: Fix triggering of client areatriggers for some shapes
Diffstat (limited to 'src')
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.cpp | 12 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.h | 9 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2LoadInfo.h | 46 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 33 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 4 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Structure.h | 27 | ||||
-rw-r--r-- | src/server/game/Entities/AreaTrigger/AreaTrigger.cpp | 71 | ||||
-rw-r--r-- | src/server/game/Entities/AreaTrigger/AreaTrigger.h | 1 | ||||
-rw-r--r-- | src/server/game/Entities/Object/Position.cpp | 101 | ||||
-rw-r--r-- | src/server/game/Entities/Object/Position.h | 9 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 41 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 2 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 21 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 2 | ||||
-rw-r--r-- | src/server/game/Grids/Notifiers/GridNotifiers.h | 6 | ||||
-rw-r--r-- | src/server/game/Handlers/MiscHandler.cpp | 6 |
16 files changed, 279 insertions, 112 deletions
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); |