From c2409af60b98ca189f66f8bdf6f95675339bf1a8 Mon Sep 17 00:00:00 2001 From: Hondacrx Date: Sun, 25 Aug 2024 19:11:12 -0400 Subject: [PATCH] Core/AreaTriggers: Fix triggering of client areatriggers for some shapes Port From (https://github.com/TrinityCore/TrinityCore/commit/b070e63fa867f7f25e73e9ef3aafbe18902a50e9) --- .../Database/Databases/HotfixDatabase.cs | 15 +++ Source/Game/DataStorage/CliDB.cs | 6 + Source/Game/DataStorage/DB2Manager.cs | 31 ++++++ Source/Game/DataStorage/Structs/L_Records.cs | 7 ++ Source/Game/DataStorage/Structs/P_Records.cs | 20 ++++ .../Game/Entities/AreaTrigger/AreaTrigger.cs | 79 +------------ Source/Game/Entities/Object/Position.cs | 105 +++++++++++++++--- Source/Game/Entities/Player/Player.cs | 41 ++++--- Source/Game/Globals/ObjectManager.cs | 17 +++ Source/Game/Handlers/MiscHandler.cs | 2 +- Source/Game/Maps/GridNotifiers.cs | 6 +- 11 files changed, 218 insertions(+), 111 deletions(-) diff --git a/Source/Framework/Database/Databases/HotfixDatabase.cs b/Source/Framework/Database/Databases/HotfixDatabase.cs index 4ddd613f0..9922cdea5 100644 --- a/Source/Framework/Database/Databases/HotfixDatabase.cs +++ b/Source/Framework/Database/Databases/HotfixDatabase.cs @@ -892,6 +892,9 @@ namespace Framework.Database "Float11, Float12, Float13, Float14, Float15, Float16, Float17, Float18, `Int1`, `Int2`, `Int3`, `Int4`, Coefficient1, Coefficient2, " + "Coefficient3, Coefficient4 FROM liquid_type WHERE (`VerifiedBuild` > 0) = ?"); + // Location.db2 + PrepareStatement(HotfixStatements.SEL_LOCATION, "SELECT ID, PosX, PosY, PosZ, Rot1, Rot2, Rot3 FROM location WHERE (`VerifiedBuild` > 0) = ?"); + // Lock.db2 PrepareStatement(HotfixStatements.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, " + @@ -984,6 +987,12 @@ namespace Framework.Database PrepareStatement(HotfixStatements.SEL_PARAGON_REPUTATION, "SELECT ID, FactionID, LevelThreshold, QuestID FROM paragon_reputation" + " WHERE (`VerifiedBuild` > 0) = ?"); + // Path.db2 + PrepareStatement(HotfixStatements.SEL_PATH, "SELECT ID, Type, SplineType, Red, Green, Blue, Alpha, Flags FROM path WHERE (`VerifiedBuild` > 0) = ?"); + + // PathNode.db2 + PrepareStatement(HotfixStatements.SEL_PATH_NODE, "SELECT ID, PathID, Sequence, LocationID FROM path_node WHERE (`VerifiedBuild` > 0) = ?"); + // Phase.db2 PrepareStatement(HotfixStatements.SEL_PHASE, "SELECT ID, Flags FROM phase WHERE (`VerifiedBuild` > 0) = ?"); @@ -2027,6 +2036,8 @@ namespace Framework.Database SEL_LIQUID_TYPE, + SEL_LOCATION, + SEL_LOCK, SEL_MAIL_TEMPLATE, @@ -2075,6 +2086,10 @@ namespace Framework.Database SEL_PARAGON_REPUTATION, + SEL_PATH, + + SEL_PATH_NODE, + SEL_PHASE, SEL_PHASE_X_PHASE_GROUP, diff --git a/Source/Game/DataStorage/CliDB.cs b/Source/Game/DataStorage/CliDB.cs index bb85e4c30..a4d8c5a62 100644 --- a/Source/Game/DataStorage/CliDB.cs +++ b/Source/Game/DataStorage/CliDB.cs @@ -230,6 +230,7 @@ namespace Game.DataStorage LFGDungeonsStorage = ReadDB2("LFGDungeons.db2", HotfixStatements.SEL_LFG_DUNGEONS, HotfixStatements.SEL_LFG_DUNGEONS_LOCALE); LightStorage = ReadDB2("Light.db2", HotfixStatements.SEL_LIGHT); LiquidTypeStorage = ReadDB2("LiquidType.db2", HotfixStatements.SEL_LIQUID_TYPE); + LocationStorage = ReadDB2("Location.db2", HotfixStatements.SEL_LOCATION); LockStorage = ReadDB2("Lock.db2", HotfixStatements.SEL_LOCK); MailTemplateStorage = ReadDB2("MailTemplate.db2", HotfixStatements.SEL_MAIL_TEMPLATE, HotfixStatements.SEL_MAIL_TEMPLATE_LOCALE); MapStorage = ReadDB2("Map.db2", HotfixStatements.SEL_MAP, HotfixStatements.SEL_MAP_LOCALE); @@ -251,6 +252,8 @@ namespace Game.DataStorage NumTalentsAtLevelStorage = ReadDB2("NumTalentsAtLevel.db2", HotfixStatements.SEL_NUM_TALENTS_AT_LEVEL); OverrideSpellDataStorage = ReadDB2("OverrideSpellData.db2", HotfixStatements.SEL_OVERRIDE_SPELL_DATA); ParagonReputationStorage = ReadDB2("ParagonReputation.db2", HotfixStatements.SEL_PARAGON_REPUTATION); + PathStorage = ReadDB2("Path.db2", HotfixStatements.SEL_PATH); + PathNodeStorage = ReadDB2("PathNode.db2", HotfixStatements.SEL_PATH_NODE); PhaseStorage = ReadDB2("Phase.db2", HotfixStatements.SEL_PHASE); PhaseXPhaseGroupStorage = ReadDB2("PhaseXPhaseGroup.db2", HotfixStatements.SEL_PHASE_X_PHASE_GROUP); PlayerConditionStorage = ReadDB2("PlayerCondition.db2", HotfixStatements.SEL_PLAYER_CONDITION, HotfixStatements.SEL_PLAYER_CONDITION_LOCALE); @@ -670,6 +673,7 @@ namespace Game.DataStorage public static DB6Storage LFGDungeonsStorage; public static DB6Storage LightStorage; public static DB6Storage LiquidTypeStorage; + public static DB6Storage LocationStorage; public static DB6Storage LockStorage; public static DB6Storage MailTemplateStorage; public static DB6Storage MapStorage; @@ -691,6 +695,8 @@ namespace Game.DataStorage public static DB6Storage NumTalentsAtLevelStorage; public static DB6Storage OverrideSpellDataStorage; public static DB6Storage ParagonReputationStorage; + public static DB6Storage PathStorage; + public static DB6Storage PathNodeStorage; public static DB6Storage PhaseStorage; public static DB6Storage PhaseXPhaseGroupStorage; public static DB6Storage PlayerConditionStorage; diff --git a/Source/Game/DataStorage/DB2Manager.cs b/Source/Game/DataStorage/DB2Manager.cs index fb61985bc..11710ecde 100644 --- a/Source/Game/DataStorage/DB2Manager.cs +++ b/Source/Game/DataStorage/DB2Manager.cs @@ -420,6 +420,31 @@ namespace Game.DataStorage if (FactionStorage.HasRecord(paragonReputation.FactionID)) _paragonReputations[paragonReputation.FactionID] = paragonReputation; + Dictionary> unsortedNodes = new(); + foreach (var (_, pathNode) in CliDB.PathNodeStorage) + { + if (CliDB.PathStorage.ContainsKey(pathNode.PathID)) + { + if (CliDB.LocationStorage.ContainsKey(pathNode.LocationID)) + { + if (!unsortedNodes.ContainsKey(pathNode.PathID)) + unsortedNodes[pathNode.PathID] = new(); + + unsortedNodes[pathNode.PathID].Add(pathNode); + } + } + } + + foreach (var (pathId, pathNodes) in unsortedNodes) + { + pathNodes.OrderBy(node => node.Sequence); + _pathNodes.AddRange(pathId, pathNodes.Select(node => + { + LocationRecord location = CliDB.LocationStorage.LookupByKey(node.LocationID); + return location.Pos; + })); + } + foreach (var group in PhaseXPhaseGroupStorage.Values) { PhaseRecord phase = PhaseStorage.LookupByKey(group.PhaseId); @@ -1740,6 +1765,11 @@ namespace Game.DataStorage return _paragonReputations.LookupByKey(factionId); } + public List GetNodesForPath(uint pathId) + { + return _pathNodes.LookupByKey(pathId); + } + public PvpDifficultyRecord GetBattlegroundBracketByLevel(uint mapid, uint level) { PvpDifficultyRecord maxEntry = null; // used for level > max listed level case @@ -2328,6 +2358,7 @@ namespace Game.DataStorage Dictionary[]> _nameGenData = new(); List[] _nameValidators = new List[(int)Locale.Total + 1]; Dictionary _paragonReputations = new(); + MultiMap _pathNodes = new(); MultiMap _phasesByGroup = new(); Dictionary _powerTypes = new(); Dictionary _pvpItemBonus = new(); diff --git a/Source/Game/DataStorage/Structs/L_Records.cs b/Source/Game/DataStorage/Structs/L_Records.cs index 8f5e1ed7e..1a48a0a8d 100644 --- a/Source/Game/DataStorage/Structs/L_Records.cs +++ b/Source/Game/DataStorage/Structs/L_Records.cs @@ -96,6 +96,13 @@ namespace Game.DataStorage public float[] Coefficient = new float[4]; } + public sealed class LocationRecord + { + public uint Id; + public Vector3 Pos; + public float[] Rot = new float[3]; + } + public sealed class LockRecord { public uint Id; diff --git a/Source/Game/DataStorage/Structs/P_Records.cs b/Source/Game/DataStorage/Structs/P_Records.cs index 0d0a72b42..620f4a330 100644 --- a/Source/Game/DataStorage/Structs/P_Records.cs +++ b/Source/Game/DataStorage/Structs/P_Records.cs @@ -13,6 +13,26 @@ namespace Game.DataStorage public int QuestID; } + public sealed class PathRecord + { + public uint Id; + public byte Type; + public byte SplineType; + public byte Red; + public byte Green; + public byte Blue; + public byte Alpha; + public byte Flags; + } + + public sealed class PathNodeRecord + { + public uint Id; + public ushort PathID; + public short Sequence; + public int LocationID; + } + public sealed class PhaseRecord { public uint Id; diff --git a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs index f3fe0128c..ea41b3277 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs @@ -678,7 +678,7 @@ namespace Game.Entities SearchUnits(targetList, GetMaxSearchRadius(), false); - targetList.RemoveAll(unit => unit.GetPositionZ() < minZ || unit.GetPositionZ() > maxZ || !CheckIsInPolygon2D(unit)); + targetList.RemoveAll(unit => unit.GetPositionZ() < minZ || unit.GetPositionZ() > maxZ || !unit.IsInPolygon2D(this, _polygonVertices)); } void SearchUnitInCylinder(List targetList) @@ -857,7 +857,7 @@ namespace Game.Entities if (MathFunctions.fuzzyEq(_verticesUpdatePreviousOrientation, newOrientation) && shape.PolygonVerticesTarget.Empty()) return; - _polygonVertices = shape.PolygonVertices; + _polygonVertices.AddRange(shape.PolygonVertices.Select(p => new Position(p.X, p.Y))); if (!shape.PolygonVerticesTarget.Empty()) { @@ -890,75 +890,6 @@ namespace Game.Entities _verticesUpdatePreviousOrientation = newOrientation; } - bool CheckIsInPolygon2D(Position pos) - { - 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 (int vertex = 0; vertex < _polygonVertices.Count; ++vertex) - { - int nextVertex; - - //repeat loop for all sets of points - if (vertex == (_polygonVertices.Count - 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].X; - float vertY_i = GetPositionY() + _polygonVertices[vertex].Y; - float vertX_j = GetPositionX() + _polygonVertices[nextVertex].X; - float vertY_j = GetPositionY() + _polygonVertices[nextVertex].Y; - - // 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 HasOverridePosition() { return m_areaTriggerData.OverrideMoveCurveX.GetValue().OverrideActive @@ -1491,9 +1422,9 @@ namespace Game.Entities float _verticesUpdatePreviousOrientation; bool _isRemoved; - Vector3 _rollPitchYaw; - Vector3 _targetRollPitchYaw; - List _polygonVertices; + Position _rollPitchYaw; + Position _targetRollPitchYaw; + List _polygonVertices; Spline _spline; bool _reachedDestination; diff --git a/Source/Game/Entities/Object/Position.cs b/Source/Game/Entities/Object/Position.cs index 58d542695..5410aa1d5 100644 --- a/Source/Game/Entities/Object/Position.cs +++ b/Source/Game/Entities/Object/Position.cs @@ -99,7 +99,7 @@ namespace Game.Entities { return NormalizeOrientation(absAngle - Orientation); } - + public float GetRelativeAngle(Position pos) { return ToRelativeAngle(GetAbsoluteAngle(pos)); @@ -227,7 +227,7 @@ namespace Game.Entities { return NormalizeOrientation(relAngle + Orientation); } - + public bool IsInDist(float x, float y, float z, float dist) { return GetExactDistSq(x, y, z) < dist * dist; @@ -252,36 +252,107 @@ namespace Game.Entities Orientation = NormalizeOrientation(orientation); } - public bool IsWithinBox(Position center, float xradius, float yradius, float zradius) + public bool IsWithinBox(Position boxOrigin, float length, float width, float height) { // 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 * Math.PI - center.GetOrientation(); + double rotation = 2 * Math.PI - boxOrigin.GetOrientation(); double sinVal = Math.Sin(rotation); double cosVal = Math.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 ((Math.Abs(dx) > xradius) || (Math.Abs(dy) > yradius) || (Math.Abs(dz) > zradius)) + float dz = GetPositionZ() - boxOrigin.GetPositionZ(); + float dx = rotX - boxOrigin.GetPositionX(); + float dy = rotY - boxOrigin.GetPositionY(); + if ((Math.Abs(dx) > length) || (Math.Abs(dy) > width) || (Math.Abs(dz) > height)) return false; return true; } - public bool IsWithinDoubleVerticalCylinder(Position center, float radius, float height) + public bool IsWithinVerticalCylinder(Position cylinderOrigin, float radius, float height, bool isDoubleVertical = false) { - float verticalDelta = GetPositionZ() - center.GetPositionZ(); - return IsInDist2d(center, radius) && Math.Abs(verticalDelta) <= height; + float verticalDelta = GetPositionZ() - cylinderOrigin.GetPositionZ(); + bool isValidPositionZ = isDoubleVertical ? Math.Abs(verticalDelta) <= height : 0 <= verticalDelta && verticalDelta <= height; + + return isValidPositionZ && IsInDist2d(cylinderOrigin, radius); + } + + public bool IsInPolygon2D(Position polygonOrigin, List vertices) + { + 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 (int vertex = 0; vertex < vertices.Count; ++vertex) + { + int nextVertex; + + //repeat loop for all sets of points + if (vertex == (vertices.Count - 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; } public bool HasInArc(float arc, Position obj, float border = 2.0f) @@ -362,7 +433,7 @@ namespace Game.Entities _mapId = mapId; Relocate(pos); } - + public void WorldRelocate(WorldLocation loc) { _mapId = loc._mapId; @@ -402,7 +473,7 @@ namespace Game.Entities var mapEntry = CliDB.MapStorage.LookupByKey(_mapId); return $"MapID: {_mapId} Map name: '{(mapEntry != null ? mapEntry.MapName[Global.WorldMgr.GetDefaultDbcLocale()] : "")}' {base.ToString()}"; } - + public override string ToString() { return $"X: {posX} Y: {posY} Z: {posZ} O: {Orientation} MapId: {_mapId}"; diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 081e1b30c..e08cd07a1 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -441,7 +441,7 @@ namespace Game.Entities if (_restMgr.HasRestFlag(RestFlag.Tavern)) { AreaTriggerRecord atEntry = CliDB.AreaTriggerStorage.LookupByKey(_restMgr.GetInnTriggerId()); - if (atEntry == null || !IsInAreaTriggerRadius(atEntry)) + if (atEntry == null || !IsInAreaTrigger(atEntry)) _restMgr.RemoveRestFlag(RestFlag.Tavern); } @@ -2425,29 +2425,38 @@ namespace Game.Entities } } - public bool IsInAreaTriggerRadius(AreaTriggerRecord trigger) + public bool IsInAreaTrigger(AreaTriggerRecord areaTrigger) { - if (trigger == null) + if (areaTrigger == null) return false; - if (GetMapId() != trigger.ContinentID && !GetPhaseShift().HasVisibleMapId(trigger.ContinentID)) + if (GetMapId() != areaTrigger.ContinentID && !GetPhaseShift().HasVisibleMapId(areaTrigger.ContinentID)) return false; - if (trigger.PhaseID != 0 || trigger.PhaseGroupID != 0 || trigger.PhaseUseFlags != 0) - if (!PhasingHandler.InDbPhaseShift(this, (PhaseUseFlagsValues)trigger.PhaseUseFlags, trigger.PhaseID, trigger.PhaseGroupID)) + if (areaTrigger.PhaseID != 0 || areaTrigger.PhaseGroupID != 0 || areaTrigger.PhaseUseFlags != 0) + if (!PhasingHandler.InDbPhaseShift(this, (PhaseUseFlagsValues)areaTrigger.PhaseUseFlags, areaTrigger.PhaseID, areaTrigger.PhaseGroupID)) return false; - if (trigger.Radius > 0.0f) + Position areaTriggerPos = new(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 = new(trigger.Pos.X, trigger.Pos.Y, trigger.Pos.Z, trigger.BoxYaw); - if (!IsWithinBox(center, trigger.BoxLength / 2.0f, trigger.BoxWidth / 2.0f, trigger.BoxHeight / 2.0f)) + case 0: // Sphere + if (!IsInDist(areaTriggerPos, areaTrigger.Radius)) + return false; + break; + case 1: // Box + if (!IsWithinBox(areaTriggerPos, areaTrigger.BoxLength / 2.0f, areaTrigger.BoxWidth / 2.0f, areaTrigger.BoxHeight / 2.0f)) + return false; + break; + case 3: // Polygon + if (!IsInPolygon2D(areaTriggerPos, ObjectMgr.GetVerticesForAreaTrigger(areaTrigger))) + return false; + break; + case 4: // Cylinder + if (!IsWithinVerticalCylinder(areaTriggerPos, areaTrigger.Radius, areaTrigger.BoxHeight)) + return false; + break; + default: return false; } diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index fd289ae87..6b1b77d11 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -751,6 +751,23 @@ namespace Game return pointsOfInterestStorage.LookupByKey(id); } + public List GetVerticesForAreaTrigger(AreaTriggerRecord areaTrigger) + { + List vertices = new(); + if (areaTrigger != null && areaTrigger.ShapeType == 3 /* Polygon */) + { + var pathNodes = Global.DB2Mgr.GetNodesForPath((uint)areaTrigger.ShapeID); + if (pathNodes != null) + vertices.AddRange(pathNodes.Select(dbcPosition => new Position(dbcPosition.X, dbcPosition.Y, dbcPosition.Z))); + + + // Drop first node (areatrigger position) + vertices.RemoveAt(0); + } + + return vertices; + } + public void LoadGraveyardZones() { uint oldMSTime = Time.GetMSTime(); diff --git a/Source/Game/Handlers/MiscHandler.cs b/Source/Game/Handlers/MiscHandler.cs index 9548208e6..6a5977d11 100644 --- a/Source/Game/Handlers/MiscHandler.cs +++ b/Source/Game/Handlers/MiscHandler.cs @@ -171,7 +171,7 @@ namespace Game return; } - if (packet.Entered && !player.IsInAreaTriggerRadius(atEntry)) + if (packet.Entered && !player.IsInAreaTrigger(atEntry)) { Log.outDebug(LogFilter.Network, "HandleAreaTrigger: Player '{0}' ({1}) too far, ignore Area Trigger ID: {2}", player.GetName(), player.GetGUID().ToString(), packet.AreaTriggerID); diff --git a/Source/Game/Maps/GridNotifiers.cs b/Source/Game/Maps/GridNotifiers.cs index bb5d2c406..c007e16bc 100644 --- a/Source/Game/Maps/GridNotifiers.cs +++ b/Source/Game/Maps/GridNotifiers.cs @@ -2061,7 +2061,7 @@ namespace Game.Maps 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)) @@ -2116,7 +2116,7 @@ namespace Game.Maps 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); } WorldObject _source; @@ -2218,7 +2218,7 @@ namespace Game.Maps 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); } WorldObject i_obj;