diff --git a/Source/Game/Movement/Generators/IdleMovement.cs b/Source/Game/Movement/Generators/IdleMovement.cs index f0fc964c2..4dae4cef0 100644 --- a/Source/Game/Movement/Generators/IdleMovement.cs +++ b/Source/Game/Movement/Generators/IdleMovement.cs @@ -47,12 +47,24 @@ namespace Game.Movement public class RotateMovementGenerator : MovementGenerator { - public RotateMovementGenerator(uint id, uint time, RotateDirection direction) + static float MIN_ANGLE_DELTA_FOR_FACING_UPDATE = 0.05f; + + uint _id; + RotateDirection _direction; + TimeTracker _duration; + float? _turnSpeed; ///< radians per sec + float? _totalTurnAngle; + uint _diffSinceLastUpdate; + + public RotateMovementGenerator(uint id, RotateDirection direction, TimeSpan? duration, float? turnSpeed, float? totalTurnAngle) { _id = id; - _duration = time; - _maxDuration = time; _direction = direction; + if (duration.HasValue) + _duration = new TimeTracker(duration.Value); + + _turnSpeed = turnSpeed; + _totalTurnAngle = totalTurnAngle; Mode = MovementGeneratorMode.Default; Priority = MovementGeneratorPriority.Normal; @@ -64,7 +76,7 @@ namespace Game.Movement { RemoveFlag(MovementGeneratorFlags.InitializationPending | MovementGeneratorFlags.Deactivated); AddFlag(MovementGeneratorFlags.Initialized); - + owner.StopMoving(); /* @@ -85,28 +97,38 @@ namespace Game.Movement public override bool Update(Unit owner, uint diff) { - if (owner == null) - return false; + _diffSinceLastUpdate += diff; - float angle = owner.GetOrientation(); - angle += diff * MathFunctions.TwoPi / _maxDuration * (_direction == RotateDirection.Left ? 1.0f : -1.0f); - angle = Math.Clamp(angle, 0.0f, MathF.PI * 2); + float currentAngle = owner.GetOrientation(); + float angleDelta = _turnSpeed.GetValueOrDefault(owner.GetSpeed(UnitMoveType.TurnRate)) * ((float)_diffSinceLastUpdate / (float)Time.InMilliseconds); - MoveSplineInit init = new(owner); - init.MoveTo(owner, false); - if (!owner.GetTransGUID().IsEmpty()) - init.DisableTransportPathTransformations(); + if (_duration != null) + _duration.Update(diff); - init.SetFacing(angle); - init.Launch(); + if (_totalTurnAngle.HasValue) + _totalTurnAngle = _totalTurnAngle - angleDelta; - if (_duration > diff) - _duration -= diff; - else + bool expired = (_duration != null && _duration.Passed()) || (_totalTurnAngle.HasValue && _totalTurnAngle < 0.0f); + + if (angleDelta >= MIN_ANGLE_DELTA_FOR_FACING_UPDATE || expired) + { + float newAngle = Position.NormalizeOrientation(currentAngle + angleDelta * (_direction == RotateDirection.Left ? 1.0f : -1.0f)); + + MoveSplineInit init = new(owner); + init.MoveTo(owner.GetPosition(), false); + if (!owner.GetTransGUID().IsEmpty()) + init.DisableTransportPathTransformations(); + init.SetFacing(newAngle); + init.Launch(); + + _diffSinceLastUpdate = 0; + } + + if (expired) { AddFlag(MovementGeneratorFlags.InformEnabled); return false; - } + } return true; } @@ -125,11 +147,6 @@ namespace Game.Movement } public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Rotate; } - - uint _id; - uint _duration; - uint _maxDuration; - RotateDirection _direction; } public class DistractMovementGenerator : MovementGenerator @@ -163,7 +180,7 @@ namespace Game.Movement init.Launch(); } - public override void Reset(Unit owner) + public override void Reset(Unit owner) { RemoveFlag(MovementGeneratorFlags.Deactivated); Initialize(owner); diff --git a/Source/Game/Movement/MotionMaster.cs b/Source/Game/Movement/MotionMaster.cs index 31e8c32f4..c5ce47732 100644 --- a/Source/Game/Movement/MotionMaster.cs +++ b/Source/Game/Movement/MotionMaster.cs @@ -1046,12 +1046,19 @@ namespace Game.Movement Add(new WaypointMovementGenerator(path, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, generatePath), MovementSlot.Default); } - public void MoveRotate(uint id, uint time, RotateDirection direction) + /// + /// Makes the Unit turn in place + /// + /// Movement identifier, later passed to script MovementInform hooks + /// Rotation direction + /// How long should this movement last, infinite if not set + /// How fast should the unit rotate, in radians per second. Uses unit's turn speed if not set + /// Total angle of the entire movement, infinite if not set + public void MoveRotate(uint id, RotateDirection direction, TimeSpan? time = null, float? turnSpeed = null, float? totalTurnAngle = null) { - if (time == 0) - return; + Log.outDebug(LogFilter.Movement, $"MotionMaster::MoveRotate: '{_owner.GetGUID()}', starts rotate (time: {time.GetValueOrDefault(TimeSpan.Zero)}ms, turnSpeed: {turnSpeed}, totalTurnAngle: {totalTurnAngle}, direction: {direction})"); - Add(new RotateMovementGenerator(id, time, direction)); + Add(new RotateMovementGenerator(id, direction, time, turnSpeed, totalTurnAngle)); } public void MoveFormation(Unit leader, float range, float angle, uint point1, uint point2)