From 5e46dd951b870ceaf6d3dea33e633a4927e0ce91 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Tue, 30 Jan 2024 21:14:55 -0500 Subject: [PATCH] Core/AreaTriggers: Implement missing functionality * Scale curves are now taken into account when searching for units inside * Implemented SpellModOp::Radius for areatrigger radius * Implemented OverrideMoveCurve updatefields - overrides areatrigger position * Implemented FacingCurve and AREATRIGGER_FLAG_HAS_ABSOLUTE_ORIENTATION * Implemented ***Target fields for all shape types * Fixed facing calculation for areatriggers with spline movement Port From (https://github.com/TrinityCore/TrinityCore/commit/c676d1477239aec4d0b235a410e5627a7070a86e) --- .../Framework/Constants/AreaTriggerConst.cs | 2 +- .../DataStorage/AreaTriggerDataStorage.cs | 6 + .../Game/Entities/AreaTrigger/AreaTrigger.cs | 406 ++++++++++++++---- .../AreaTrigger/AreaTriggerTemplate.cs | 18 +- Source/Game/Entities/Object/WorldObject.cs | 2 +- Source/Game/Entities/Unit/Unit.cs | 11 +- 6 files changed, 344 insertions(+), 101 deletions(-) diff --git a/Source/Framework/Constants/AreaTriggerConst.cs b/Source/Framework/Constants/AreaTriggerConst.cs index 1ff9f8e68..0b7acf324 100644 --- a/Source/Framework/Constants/AreaTriggerConst.cs +++ b/Source/Framework/Constants/AreaTriggerConst.cs @@ -5,7 +5,7 @@ namespace Framework.Constants { public enum AreaTriggerFlags { - HasAbsoluteOrientation = 0x01, // Nyi + HasAbsoluteOrientation = 0x01, HasDynamicShape = 0x02, // Implemented For Spheres HasAttached = 0x04, HasFaceMovementDir = 0x08, diff --git a/Source/Game/DataStorage/AreaTriggerDataStorage.cs b/Source/Game/DataStorage/AreaTriggerDataStorage.cs index 2cfbb678d..2be835dd1 100644 --- a/Source/Game/DataStorage/AreaTriggerDataStorage.cs +++ b/Source/Game/DataStorage/AreaTriggerDataStorage.cs @@ -196,6 +196,12 @@ namespace Game.DataStorage createProperties.PolygonVertices = verticesByCreateProperties[createProperties.Id]; createProperties.PolygonVerticesTarget = verticesTargetByCreateProperties[createProperties.Id]; + if (!createProperties.PolygonVerticesTarget.Empty() && createProperties.PolygonVertices.Count != createProperties.PolygonVerticesTarget.Count) + { + Log.outError(LogFilter.Sql, $"Table `areatrigger_create_properties_polygon_vertex` has invalid target vertices, either all or none vertices must have a corresponding target vertex (AreaTriggerCreatePropertiesId: {createProperties.Id})."); + createProperties.PolygonVerticesTarget.Clear(); + } + createProperties.SplinePoints = splinesByCreateProperties[createProperties.Id]; _areaTriggerCreateProperties[createProperties.Id] = createProperties; diff --git a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs index 8360e7a83..712df53b3 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs @@ -19,7 +19,7 @@ namespace Game.Entities { public AreaTrigger() : base(false) { - _previousCheckOrientation = float.PositiveInfinity; + _verticesUpdatePreviousOrientation = float.PositiveInfinity; _reachedDestination = true; ObjectTypeMask |= TypeMask.AreaTrigger; @@ -83,6 +83,7 @@ namespace Game.Entities SetMap(caster.GetMap()); Relocate(pos); + RelocateStationaryPosition(pos); if (!IsPositionValid()) { Log.outError(LogFilter.AreaTrigger, $"AreaTrigger (areaTriggerCreatePropertiesId: {areaTriggerCreatePropertiesId}) not created. Invalid coordinates (X: {GetPositionX()} Y: {GetPositionY()})"); @@ -110,7 +111,6 @@ namespace Game.Entities SetObjectScale(1.0f); _shape = GetCreateProperties().Shape; - _maxSearchRadius = GetCreateProperties().GetMaxSearchRadius(); var areaTriggerData = m_values.ModifyValue(m_areaTriggerData); SetUpdateFieldValue(areaTriggerData.ModifyValue(m_areaTriggerData.Caster), caster.GetGUID()); @@ -125,11 +125,25 @@ namespace Game.Entities SetUpdateFieldValue(ref spellCastVisual.ScriptVisualID, spellVisual.ScriptVisualID); SetUpdateFieldValue(areaTriggerData.ModifyValue(m_areaTriggerData.TimeToTargetScale), GetCreateProperties().TimeToTargetScale != 0 ? GetCreateProperties().TimeToTargetScale : m_areaTriggerData.Duration); - SetUpdateFieldValue(areaTriggerData.ModifyValue(m_areaTriggerData.BoundsRadius2D), GetMaxSearchRadius()); + SetUpdateFieldValue(areaTriggerData.ModifyValue(m_areaTriggerData.BoundsRadius2D), GetCreateProperties().GetMaxSearchRadius()); SetUpdateFieldValue(areaTriggerData.ModifyValue(m_areaTriggerData.DecalPropertiesID), GetCreateProperties().DecalPropertiesId); SetScaleCurve(areaTriggerData.ModifyValue(m_areaTriggerData.ExtraScaleCurve), GetCreateProperties().ExtraScale); + Player modOwner = caster.GetSpellModOwner(); + if (modOwner != null) + { + float multiplier = 1.0f; + int flat = 0; + modOwner.GetSpellModValues(spellInfo, SpellModOp.Radius, spell, (float)m_areaTriggerData.BoundsRadius2D, ref flat, ref multiplier); + if (multiplier != 1.0f) + { + AreaTriggerScaleCurveTemplate overrideScale = new(); + overrideScale.Curve = multiplier; + SetScaleCurve(areaTriggerData.ModifyValue(m_areaTriggerData.OverrideScaleCurve), overrideScale); + } + } + VisualAnim visualAnim = areaTriggerData.ModifyValue(m_areaTriggerData.VisualAnim); SetUpdateFieldValue(visualAnim.ModifyValue(visualAnim.AnimationDataID), GetCreateProperties().AnimId); SetUpdateFieldValue(visualAnim.ModifyValue(visualAnim.AnimKitID), GetCreateProperties().AnimKitId); @@ -231,6 +245,7 @@ namespace Game.Entities { SetMap(map); Relocate(position.SpawnPoint); + RelocateStationaryPosition(position.SpawnPoint); if (!IsPositionValid()) { Log.outError(LogFilter.AreaTrigger, $"AreaTriggerServer (id {areaTriggerTemplate.Id}) not created. Invalid coordinates (X: {GetPositionX()} Y: {GetPositionY()})"); @@ -248,7 +263,8 @@ namespace Game.Entities SetObjectScale(1.0f); SetDuration(-1); - SetUpdateFieldValue(m_areaTriggerData.ModifyValue(m_areaTriggerData.BoundsRadius2D), GetMaxSearchRadius()); + _shape = position.Shape; + if (position.SpellForVisuals.HasValue) { SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(position.SpellForVisuals.Value, Difficulty.None); @@ -259,6 +275,7 @@ namespace Game.Entities SetUpdateFieldValue(ref spellCastVisual.ScriptVisualID, 0u); } + SetUpdateFieldValue(m_areaTriggerData.ModifyValue(m_areaTriggerData.BoundsRadius2D), _shape.GetMaxSearchRadius()); if (IsServerSide()) SetUpdateFieldValue(m_areaTriggerData.ModifyValue(m_areaTriggerData.DecalPropertiesID), 24u); // blue decal, for .debug areatrigger visibility @@ -267,11 +284,8 @@ namespace Game.Entities VisualAnim visualAnim = m_areaTriggerData.ModifyValue(m_areaTriggerData.VisualAnim); SetUpdateFieldValue(visualAnim.ModifyValue(visualAnim.AnimationDataID), -1); - _shape = position.Shape; - _maxSearchRadius = _shape.GetMaxSearchRadius(); - if (position.PhaseUseFlags != 0 || position.PhaseId != 0 || position.PhaseGroup != 0) - PhasingHandler.InitDbPhaseShift(GetPhaseShift(), (PhaseUseFlagsValues)position.PhaseUseFlags, position.PhaseId, position.PhaseGroup); + PhasingHandler.InitDbPhaseShift(GetPhaseShift(), position.PhaseUseFlags, position.PhaseId, position.PhaseGroup); UpdateShape(); @@ -290,7 +304,11 @@ namespace Game.Entities if (!IsServerSide()) { // "If" order matter here, Orbit > Attached > Splines - if (HasOrbit()) + if (HasOverridePosition()) + { + UpdateOverridePosition(); + } + else if (HasOrbit()) { UpdateOrbitPosition(diff); } @@ -298,10 +316,34 @@ namespace Game.Entities { Unit target = GetTarget(); if (target != null) - GetMap().AreaTriggerRelocation(this, target.GetPositionX(), target.GetPositionY(), target.GetPositionZ(), target.GetOrientation()); + { + float orientation = 0.0f; + if (GetCreateProperties().FacingCurveId != 0) + orientation = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().FacingCurveId, GetProgress()); + + if (GetTemplate() == null || !GetTemplate().HasFlag(AreaTriggerFlags.HasAbsoluteOrientation)) + orientation += target.GetOrientation(); + + GetMap().AreaTriggerRelocation(this, target.GetPositionX(), target.GetPositionY(), target.GetPositionZ(), orientation); + } + } + else if (HasSplines()) + { + UpdateSplinePosition(diff); } else - UpdateSplinePosition(diff); + { + if (GetCreateProperties().FacingCurveId != 0) + { + float orientation = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().FacingCurveId, GetProgress()); + if (GetTemplate() == null || !GetTemplate().HasFlag(AreaTriggerFlags.HasAbsoluteOrientation)) + orientation += GetStationaryO(); + + SetOrientation(orientation); + } + + UpdateShape(); + } } if (GetDuration() != -1) @@ -326,6 +368,57 @@ namespace Game.Entities AddObjectToRemoveList(); } + void SetOverrideScaleCurve(float overrideScale) + { + SetScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideScaleCurve), overrideScale); + } + + void SetOverrideScaleCurve(Vector2[] points, uint? startTimeOffset, CurveInterpolationMode interpolation) + { + SetScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideScaleCurve), points, startTimeOffset, interpolation); + } + + void ClearOverrideScaleCurve() + { + ClearScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideScaleCurve)); + } + + void SetExtraScaleCurve(float extraScale) + { + SetScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.ExtraScaleCurve), extraScale); + } + + void SetExtraScaleCurve(Vector2[] points, uint? startTimeOffset, CurveInterpolationMode interpolation) + { + SetScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.ExtraScaleCurve), points, startTimeOffset, interpolation); + } + + void ClearExtraScaleCurve() + { + ClearScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.ExtraScaleCurve)); + } + + void SetOverrideMoveCurve(float x, float y, float z) + { + SetScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideMoveCurveX), x); + SetScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideMoveCurveY), y); + SetScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideMoveCurveZ), z); + } + + void SetOverrideMoveCurve(Vector2[] xCurvePoints, Vector2[] yCurvePoints, Vector2[] zCurvePoints, uint? startTimeOffset, CurveInterpolationMode interpolation) + { + SetScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideMoveCurveX), xCurvePoints, startTimeOffset, interpolation); + SetScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideMoveCurveY), yCurvePoints, startTimeOffset, interpolation); + SetScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideMoveCurveZ), zCurvePoints, startTimeOffset, interpolation); + } + + void ClearOverrideMoveCurve() + { + ClearScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideMoveCurveX)); + ClearScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideMoveCurveY)); + ClearScaleCurve(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.OverrideMoveCurveZ)); + } + public void SetDuration(int newDuration) { _duration = newDuration; @@ -347,18 +440,46 @@ namespace Game.Entities }); } - float GetProgress() + float CalcCurrentScale() { - return GetTimeSinceCreated() < GetTimeToTargetScale() ? (float)GetTimeSinceCreated() / GetTimeToTargetScale() : 1.0f; + float scale = 1.0f; + if (m_areaTriggerData.OverrideScaleCurve.GetValue().OverrideActive) + scale *= Math.Max(GetScaleCurveValue(m_areaTriggerData.OverrideScaleCurve, m_areaTriggerData.TimeToTargetScale), 0.000001f); + else + { + AreaTriggerCreateProperties createProperties = GetCreateProperties(); + if (createProperties != null && createProperties.ScaleCurveId != 0) + scale *= Math.Max(Global.DB2Mgr.GetCurveValueAt(createProperties.ScaleCurveId, GetScaleCurveProgress(m_areaTriggerData.OverrideScaleCurve, m_areaTriggerData.TimeToTargetScale)), 0.000001f); + } + + scale *= Math.Max(GetScaleCurveValue(m_areaTriggerData.ExtraScaleCurve, m_areaTriggerData.TimeToTargetExtraScale), 0.000001f); + + return scale; } - float GetScaleCurveValue(ScaleCurve scaleCurve, float x) + float GetProgress() + { + if (_totalDuration <= 0) + return 1.0f; + + return Math.Clamp((float)GetTimeSinceCreated() / (float)GetTotalDuration(), 0.0f, 1.0f); + } + + float GetScaleCurveProgress(ScaleCurve scaleCurve, uint timeTo) + { + if (timeTo == 0) + return 0.0f; + + return Math.Clamp((float)(GetTimeSinceCreated() - scaleCurve.StartTimeOffset) / (float)timeTo, 0.0f, 1.0f); + } + + float GetScaleCurveValueAtProgress(ScaleCurve scaleCurve, float x) { Cypher.Assert(scaleCurve.OverrideActive, "ScaleCurve must be active to evaluate it"); // unpack ParameterCurve - if ((scaleCurve.ParameterCurve & 1) != 0) - return BitConverter.UInt32BitsToSingle((uint)(scaleCurve.ParameterCurve & ~1)); + if ((scaleCurve.ParameterCurve & 1u) != 0) + return BitConverter.UInt32BitsToSingle((uint)(scaleCurve.ParameterCurve & ~1u)); Vector2[] points = new Vector2[2]; for (var i = 0; i < scaleCurve.Points.GetSize(); ++i) @@ -370,6 +491,36 @@ namespace Game.Entities return Global.DB2Mgr.GetCurveValueAt(mode, points.AsSpan(0, pointCount).ToArray(), x); } + float GetScaleCurveValue(ScaleCurve scaleCurve, uint timeTo) + { + return GetScaleCurveValueAtProgress(scaleCurve, GetScaleCurveProgress(scaleCurve, timeTo)); + } + + void SetScaleCurve(ScaleCurve scaleCurve, float constantValue) + { + AreaTriggerScaleCurveTemplate curveTemplate = new(); + curveTemplate.Curve = constantValue; + SetScaleCurve(scaleCurve, curveTemplate); + } + + void SetScaleCurve(ScaleCurve scaleCurve, Vector2[] points, uint? startTimeOffset, CurveInterpolationMode interpolation) + { + AreaTriggerScaleCurvePointsTemplate curve = new(); + curve.Mode = interpolation; + curve.Points = points; + + AreaTriggerScaleCurveTemplate curveTemplate = new(); + curveTemplate.StartTimeOffset = startTimeOffset.GetValueOrDefault(GetTimeSinceCreated()); + curveTemplate.CurveTemplate = curve; + + SetScaleCurve(scaleCurve, curveTemplate); + } + + void ClearScaleCurve(ScaleCurve scaleCurve) + { + SetScaleCurve(scaleCurve, null); + } + void SetScaleCurve(ScaleCurve scaleCurve, AreaTriggerScaleCurveTemplate curve) { if (curve == null) @@ -437,7 +588,7 @@ namespace Game.Entities } } } - + void UpdateTargetList() { List targetList = new(); @@ -493,99 +644,111 @@ namespace Game.Entities void SearchUnitInSphere(List targetList) { - float radius = _shape.SphereDatas.Radius; - if (GetTemplate() != null && GetTemplate().HasFlag(AreaTriggerFlags.HasDynamicShape)) - { - if (GetCreateProperties().MorphCurveId != 0) - { - radius = MathFunctions.Lerp(_shape.SphereDatas.Radius, _shape.SphereDatas.RadiusTarget, Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().MorphCurveId, GetProgress())); - } - } + float progress = GetProgress(); + if (GetCreateProperties().MorphCurveId != 0) + progress = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().MorphCurveId, progress); + + float scale = CalcCurrentScale(); + float radius = MathFunctions.Lerp(_shape.SphereDatas.Radius, _shape.SphereDatas.RadiusTarget, progress) * scale; SearchUnits(targetList, radius, true); } void SearchUnitInBox(List targetList) { - SearchUnits(targetList, GetMaxSearchRadius(), false); - - Position boxCenter = GetPosition(); - float extentsX, extentsY, extentsZ; + float progress = GetProgress(); + if (GetCreateProperties().MorphCurveId != 0) + progress = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().MorphCurveId, progress); unsafe { - extentsX = _shape.BoxDatas.Extents[0]; - extentsY = _shape.BoxDatas.Extents[1]; - extentsZ = _shape.BoxDatas.Extents[2]; - } + float scale = CalcCurrentScale(); + float extentsX = MathFunctions.Lerp(_shape.BoxDatas.Extents[0], _shape.BoxDatas.ExtentsTarget[0], progress) * scale; + float extentsY = MathFunctions.Lerp(_shape.BoxDatas.Extents[1], _shape.BoxDatas.ExtentsTarget[1], progress) * scale; + float extentsZ = MathFunctions.Lerp(_shape.BoxDatas.Extents[2], _shape.BoxDatas.ExtentsTarget[2], progress) * scale; + float radius = MathF.Sqrt(extentsX * extentsX + extentsY * extentsY); - targetList.RemoveAll(unit => !unit.IsWithinBox(boxCenter, extentsX, extentsY, extentsZ / 2)); + SearchUnits(targetList, radius, false); + + Position boxCenter = GetPosition(); + targetList.RemoveAll(unit => !unit.IsWithinBox(boxCenter, extentsX, extentsY, extentsZ / 2)); + } } void SearchUnitInPolygon(List targetList) { - SearchUnits(targetList, GetMaxSearchRadius(), false); + float progress = GetProgress(); + if (GetCreateProperties().MorphCurveId != 0) + progress = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().MorphCurveId, progress); - float height = _shape.PolygonDatas.Height; + float height = MathFunctions.Lerp(_shape.PolygonDatas.Height, _shape.PolygonDatas.HeightTarget, progress); float minZ = GetPositionZ() - height; float maxZ = GetPositionZ() + height; - targetList.RemoveAll(unit => !CheckIsInPolygon2D(unit) || unit.GetPositionZ() < minZ || unit.GetPositionZ() > maxZ); + SearchUnits(targetList, GetMaxSearchRadius(), false); + + targetList.RemoveAll(unit => unit.GetPositionZ() < minZ || unit.GetPositionZ() > maxZ || !CheckIsInPolygon2D(unit)); } void SearchUnitInCylinder(List targetList) { - SearchUnits(targetList, GetMaxSearchRadius(), false); + float progress = GetProgress(); + if (GetCreateProperties().MorphCurveId != 0) + progress = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().MorphCurveId, progress); + + float scale = CalcCurrentScale(); + float radius = MathFunctions.Lerp(_shape.CylinderDatas.Radius, _shape.CylinderDatas.RadiusTarget, progress) * scale; + float height = MathFunctions.Lerp(_shape.CylinderDatas.Height, _shape.CylinderDatas.HeightTarget, progress); + if (!m_areaTriggerData.HeightIgnoresScale) + height *= scale; - float height = _shape.CylinderDatas.Height; float minZ = GetPositionZ() - height; float maxZ = GetPositionZ() + height; + SearchUnits(targetList, radius, false); + targetList.RemoveAll(unit => unit.GetPositionZ() < minZ || unit.GetPositionZ() > maxZ); } void SearchUnitInDisk(List targetList) { - float innerRadius = _shape.DiskDatas.InnerRadius; - float outerRadius = _shape.DiskDatas.OuterRadius; - float height = _shape.DiskDatas.Height; + float progress = GetProgress(); + if (GetCreateProperties().MorphCurveId != 0) + progress = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().MorphCurveId, progress); - if (GetTemplate() != null && GetTemplate().HasFlag(AreaTriggerFlags.HasDynamicShape)) - { - float progress = GetProgress(); - if (GetCreateProperties().MorphCurveId != 0) - progress = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().MorphCurveId, progress); - - innerRadius = MathFunctions.Lerp(_shape.DiskDatas.InnerRadius, _shape.DiskDatas.InnerRadiusTarget, progress); - outerRadius = MathFunctions.Lerp(_shape.DiskDatas.OuterRadius, _shape.DiskDatas.OuterRadiusTarget, progress); - height = MathFunctions.Lerp(_shape.DiskDatas.Height, _shape.DiskDatas.HeightTarget, progress); - } - - SearchUnits(targetList, outerRadius, false); + float scale = CalcCurrentScale(); + float innerRadius = MathFunctions.Lerp(_shape.DiskDatas.InnerRadius, _shape.DiskDatas.InnerRadiusTarget, progress) * scale; + float outerRadius = MathFunctions.Lerp(_shape.DiskDatas.OuterRadius, _shape.DiskDatas.OuterRadiusTarget, progress) * scale; + float height = MathFunctions.Lerp(_shape.DiskDatas.Height, _shape.DiskDatas.HeightTarget, progress); + if (!m_areaTriggerData.HeightIgnoresScale) + height *= scale; float minZ = GetPositionZ() - height; float maxZ = GetPositionZ() + height; + SearchUnits(targetList, outerRadius, false); + targetList.RemoveAll(unit => unit.IsInDist2d(this, innerRadius) || unit.GetPositionZ() < minZ || unit.GetPositionZ() > maxZ); } void SearchUnitInBoundedPlane(List targetList) { - SearchUnits(targetList, GetMaxSearchRadius(), false); - - Position boxCenter = GetPosition(); - float extentsX, extentsY; + float progress = GetProgress(); + if (GetCreateProperties().MorphCurveId != 0) + progress = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().MorphCurveId, progress); unsafe { - extentsX = _shape.BoxDatas.Extents[0]; - extentsY = _shape.BoxDatas.Extents[1]; - } + float scale = CalcCurrentScale(); + float extentsX = MathFunctions.Lerp(_shape.BoundedPlaneDatas.Extents[0], _shape.BoundedPlaneDatas.ExtentsTarget[0], progress) * scale; + float extentsY = MathFunctions.Lerp(_shape.BoundedPlaneDatas.Extents[1], _shape.BoundedPlaneDatas.ExtentsTarget[1], progress) * scale; + float radius = MathF.Sqrt(extentsX * extentsX + extentsY * extentsY); - targetList.RemoveAll(unit => - { - return !unit.IsWithinBox(boxCenter, extentsX, extentsY, MapConst.MapSize); - }); + SearchUnits(targetList, radius, false); + + Position boxCenter = GetPosition(); + targetList.RemoveAll(unit => !unit.IsWithinBox(boxCenter, extentsX, extentsY, MapConst.MapSize)); + } } void HandleUnitEnterExit(List newTargetList) @@ -679,16 +842,37 @@ namespace Game.Entities return 0; } - void UpdatePolygonOrientation() + float GetMaxSearchRadius() + { + return m_areaTriggerData.BoundsRadius2D * CalcCurrentScale(); + } + + void UpdatePolygonVertices() { float newOrientation = GetOrientation(); // No need to recalculate, orientation didn't change - if (MathFunctions.fuzzyEq(_previousCheckOrientation, newOrientation)) + if (MathFunctions.fuzzyEq(_verticesUpdatePreviousOrientation, newOrientation) && GetCreateProperties().PolygonVerticesTarget.Empty()) return; _polygonVertices = GetCreateProperties().PolygonVertices; + if (!GetCreateProperties().PolygonVerticesTarget.Empty()) + { + float progress = GetProgress(); + if (GetCreateProperties().MorphCurveId != 0) + progress = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().MorphCurveId, progress); + + for (var i = 0; i < _polygonVertices.Count; ++i) + { + Vector2 vertex = _polygonVertices[i]; + Vector2 vertexTarget = GetCreateProperties().PolygonVerticesTarget[i]; + + vertex.X = MathFunctions.Lerp(vertex.X, vertexTarget.X, progress); + vertex.Y = MathFunctions.Lerp(vertex.Y, vertexTarget.Y, progress); + } + } + float angleSin = (float)Math.Sin(newOrientation); float angleCos = (float)Math.Cos(newOrientation); @@ -701,7 +885,7 @@ namespace Game.Entities vertice.Y = vertice.Y * angleCos + vertice.X * angleSin; } - _previousCheckOrientation = newOrientation; + _verticesUpdatePreviousOrientation = newOrientation; } bool CheckIsInPolygon2D(Position pos) @@ -773,10 +957,17 @@ namespace Game.Entities return locatedInPolygon; } + bool HasOverridePosition() + { + return m_areaTriggerData.OverrideMoveCurveX.GetValue().OverrideActive + && m_areaTriggerData.OverrideMoveCurveY.GetValue().OverrideActive + && m_areaTriggerData.OverrideMoveCurveZ.GetValue().OverrideActive; + } + public void UpdateShape() { if (_shape.IsPolygon()) - UpdatePolygonOrientation(); + UpdatePolygonVertices(); } bool UnitFitToActionRequirement(Unit unit, Unit caster, AreaTriggerAction action) @@ -978,6 +1169,8 @@ namespace Game.Entities // AreaTrigger make exactly "Duration / TimeToTarget" loops during his life time float pathProgress = (float)cmi.ElapsedTimeForMovement / cmi.TimeToTarget; + if (GetCreateProperties().MoveCurveId != 0) + pathProgress = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().MoveCurveId, pathProgress); // We already made one circle and can't loop if (!cmi.CanLoop) @@ -1002,7 +1195,17 @@ namespace Game.Entities float y = centerPos.GetPositionY() + (radius * (float)Math.Sin(angle)); float z = centerPos.GetPositionZ() + cmi.ZOffset; - return new Position(x, y, z, angle); + float orientation = 0.0f; + if (GetCreateProperties().FacingCurveId != 0) + orientation = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().FacingCurveId, GetProgress()); + + if (GetTemplate() == null || !GetTemplate().HasFlag(AreaTriggerFlags.HasAbsoluteOrientation)) + { + orientation += angle; + orientation += cmi.CounterClockwise ? MathFunctions.PiOver4 : -MathFunctions.PiOver4; + } + + return new Position(x, y, z, orientation); } void UpdateOrbitPosition(uint diff) @@ -1024,9 +1227,6 @@ namespace Game.Entities if (_reachedDestination) return; - if (!HasSplines()) - return; - _movementTime += diff; if (_movementTime >= GetTimeToTarget()) @@ -1067,11 +1267,15 @@ namespace Game.Entities Vector3 currentPosition; _spline.Evaluate_Percent(lastPositionIndex, percentFromLastPoint, out currentPosition); - float orientation = GetOrientation(); - if (GetTemplate() != null && GetTemplate().HasFlag(AreaTriggerFlags.HasFaceMovementDir)) + float orientation = GetStationaryO(); + if (GetCreateProperties().FacingCurveId != 0) + orientation += Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().FacingCurveId, GetProgress()); + + if (GetTemplate() != null && !GetTemplate().HasFlag(AreaTriggerFlags.HasAbsoluteOrientation) && GetTemplate().HasFlag(AreaTriggerFlags.HasFaceMovementDir)) { - Vector3 nextPoint = _spline.GetPoint(lastPositionIndex + 1); - orientation = GetAbsoluteAngle(nextPoint.X, nextPoint.Y); + _spline.Evaluate_Derivative(lastPositionIndex, percentFromLastPoint, out Vector3 derivative); + if (derivative.X != 0.0f || derivative.Y != 0.0f) + orientation += MathF.Atan2(derivative.Y, derivative.X); } GetMap().AreaTriggerRelocation(this, currentPosition.X, currentPosition.Y, currentPosition.Z, orientation); @@ -1085,6 +1289,26 @@ namespace Game.Entities } } + void UpdateOverridePosition() + { + float progress = GetScaleCurveProgress(m_areaTriggerData.OverrideMoveCurveX, m_areaTriggerData.TimeToTargetPos); + + float x = GetScaleCurveValueAtProgress(m_areaTriggerData.OverrideMoveCurveX, progress); + float y = GetScaleCurveValueAtProgress(m_areaTriggerData.OverrideMoveCurveY, progress); + float z = GetScaleCurveValueAtProgress(m_areaTriggerData.OverrideMoveCurveZ, progress); + float orientation = GetOrientation(); + + if (GetCreateProperties().FacingCurveId != 0) + { + orientation = Global.DB2Mgr.GetCurveValueAt(GetCreateProperties().FacingCurveId, GetProgress()); + if (GetTemplate() == null || !GetTemplate().HasFlag(AreaTriggerFlags.HasAbsoluteOrientation)) + orientation += GetStationaryO(); + } + + GetMap().AreaTriggerRelocation(this, x, y, z, orientation); + } + + void AI_Initialize() { AI_Destroy(); @@ -1117,7 +1341,7 @@ namespace Game.Entities return false; } - + public override void BuildValuesCreate(WorldPacket data, Player target) { UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); @@ -1197,12 +1421,31 @@ namespace Game.Entities } } + public override float GetStationaryX() { return _stationaryPosition.GetPositionX(); } + public override float GetStationaryY() { return _stationaryPosition.GetPositionY(); } + public override float GetStationaryZ() { return _stationaryPosition.GetPositionZ(); } + public override float GetStationaryO() { return _stationaryPosition.GetOrientation(); } + void RelocateStationaryPosition(Position pos) { _stationaryPosition.Relocate(pos); } + public bool IsRemoved() { return _isRemoved; } public uint GetSpellId() { return m_areaTriggerData.SpellID; } public AuraEffect GetAuraEffect() { return _aurEff; } public uint GetTimeSinceCreated() { return _timeSinceCreated; } + + public void SetHeightIgnoresScale(bool heightIgnoresScale) { SetUpdateFieldValue(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.HeightIgnoresScale), heightIgnoresScale); } + public uint GetTimeToTarget() { return m_areaTriggerData.TimeToTarget; } + public void SetTimeToTarget(uint timeToTarget) { SetUpdateFieldValue(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.TimeToTarget), timeToTarget); } + public uint GetTimeToTargetScale() { return m_areaTriggerData.TimeToTargetScale; } + public void SetTimeToTargetScale(uint timeToTargetScale) { SetUpdateFieldValue(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.TimeToTargetScale), timeToTargetScale); } + + public uint GetTimeToTargetExtraScale() { return m_areaTriggerData.TimeToTargetExtraScale; } + public void SetTimeToTargetExtraScale(uint timeToTargetExtraScale) { SetUpdateFieldValue(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.TimeToTargetExtraScale), timeToTargetExtraScale); } + + public uint GetTimeToTargetPos() { return m_areaTriggerData.TimeToTargetPos; } + public void SetTimeToTargetPos(uint timeToTargetPos) { SetUpdateFieldValue(m_values.ModifyValue(m_areaTriggerData).ModifyValue(m_areaTriggerData.TimeToTargetPos), timeToTargetPos); } + public int GetDuration() { return _duration; } public int GetTotalDuration() { return _totalDuration; } @@ -1216,7 +1459,6 @@ namespace Game.Entities public ObjectGuid GetCasterGuid() { return m_areaTriggerData.Caster; } public AreaTriggerShapeInfo GetShape() { return _shape; } - float GetMaxSearchRadius() { return _maxSearchRadius; } public Vector3 GetRollPitchYaw() { return _rollPitchYaw; } public Vector3 GetTargetRollPitchYaw() { return _targetRollPitchYaw; } @@ -1224,7 +1466,7 @@ namespace Game.Entities public Spline GetSpline() { return _spline; } public uint GetElapsedTimeForMovement() { return GetTimeSinceCreated(); } // @todo: research the right value, in sniffs both timers are nearly identical - public AreaTriggerOrbitInfo GetCircularMovementInfo() { return _orbitInfo; } + public AreaTriggerOrbitInfo GetOrbit() { return _orbitInfo; } AreaTriggerFieldData m_areaTriggerData; @@ -1234,12 +1476,12 @@ namespace Game.Entities AuraEffect _aurEff; + Position _stationaryPosition; AreaTriggerShapeInfo _shape; - float _maxSearchRadius; int _duration; int _totalDuration; uint _timeSinceCreated; - float _previousCheckOrientation; + float _verticesUpdatePreviousOrientation; bool _isRemoved; Vector3 _rollPitchYaw; diff --git a/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs b/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs index a8cf2f9aa..56922f203 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs @@ -163,13 +163,15 @@ namespace Game.Entities case AreaTriggerTypes.Sphere: return Math.Max(SphereDatas.Radius, SphereDatas.RadiusTarget); case AreaTriggerTypes.Box: - return MathF.Sqrt(BoxDatas.Extents[0] * BoxDatas.Extents[0] + BoxDatas.Extents[1] * BoxDatas.Extents[1]); + return MathF.Sqrt(Math.Max(BoxDatas.Extents[0] * BoxDatas.Extents[0] + BoxDatas.Extents[1] * BoxDatas.Extents[1], + BoxDatas.ExtentsTarget[0] * BoxDatas.ExtentsTarget[0] + BoxDatas.ExtentsTarget[1] * BoxDatas.ExtentsTarget[1])); case AreaTriggerTypes.Cylinder: return Math.Max(CylinderDatas.Radius, CylinderDatas.RadiusTarget); case AreaTriggerTypes.Disk: return Math.Max(DiskDatas.OuterRadius, DiskDatas.OuterRadiusTarget); case AreaTriggerTypes.BoundedPlane: - return MathF.Sqrt(BoundedPlaneDatas.Extents[0] * BoundedPlaneDatas.Extents[0] / 4 + BoundedPlaneDatas.Extents[1] * BoundedPlaneDatas.Extents[1] / 4); + return MathF.Sqrt(Math.Max(BoundedPlaneDatas.Extents[0] * BoundedPlaneDatas.Extents[0] / 4 + BoundedPlaneDatas.Extents[1] * BoundedPlaneDatas.Extents[1] / 4, + BoundedPlaneDatas.ExtentsTarget[0] * BoundedPlaneDatas.ExtentsTarget[0] / 4 + BoundedPlaneDatas.ExtentsTarget[1] * BoundedPlaneDatas.ExtentsTarget[1] / 4)); } return 0.0f; @@ -246,20 +248,18 @@ namespace Game.Entities Position center = new(0.0f, 0.0f); float maxSearchRadius = 0.0f; - foreach (var vertice in PolygonVertices) - { - float pointDist = center.GetExactDist2d(vertice.X, vertice.Y); + foreach (var vertex in PolygonVertices) + maxSearchRadius = Math.Max(maxSearchRadius, center.GetExactDist2d(vertex.X, vertex.Y)); - if (pointDist > maxSearchRadius) - maxSearchRadius = pointDist; - } + foreach (var vertex in PolygonVerticesTarget) + maxSearchRadius = Math.Max(maxSearchRadius, center.GetExactDist2d(vertex.X, vertex.Y)); return maxSearchRadius; } return Shape.GetMaxSearchRadius(); } - + public uint Id; public AreaTriggerTemplate Template; diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 1ea26fc1e..0841838d6 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -574,7 +574,7 @@ namespace Game.Entities // *data << *areaTrigger.GetMovementScript(); // AreaTriggerMovementScriptInfo if (hasOrbit) - areaTrigger.GetCircularMovementInfo().Write(data); + areaTrigger.GetOrbit().Write(data); } if (flags.GameObject) diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index d6bc4e5f8..bbca9a781 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -108,7 +108,8 @@ namespace Game.Entities ASSERT(m_ownedAuras.empty()); ASSERT(m_removedAuras.empty()); ASSERT(m_gameObj.empty()); - ASSERT(m_dynObj.empty());*/ + ASSERT(m_dynObj.empty()); + ASSERT(m_areaTrigger.empty());*/ base.Dispose(); } @@ -813,9 +814,6 @@ namespace Game.Entities public void RemoveAreaTrigger(uint spellId) { - if (m_areaTrigger.Empty()) - return; - for (var i = 0; i < m_areaTrigger.Count; ++i) { AreaTrigger areaTrigger = m_areaTrigger[i]; @@ -826,9 +824,6 @@ namespace Game.Entities public void RemoveAreaTrigger(AuraEffect aurEff) { - if (m_areaTrigger.Empty()) - return; - foreach (AreaTrigger areaTrigger in m_areaTrigger) { if (areaTrigger.GetAuraEffect() == aurEff) @@ -842,7 +837,7 @@ namespace Game.Entities public void RemoveAllAreaTriggers() { while (!m_areaTrigger.Empty()) - m_areaTrigger[0].Remove(); + m_areaTrigger.Last()?.Remove(); } public NPCFlags GetNpcFlags() { return (NPCFlags)m_unitData.NpcFlags[0]; }