diff --git a/OpenRA.Mods.Cnc/Activities/Leap.cs b/OpenRA.Mods.Cnc/Activities/Leap.cs index e37af852ea..ff5220c25b 100644 --- a/OpenRA.Mods.Cnc/Activities/Leap.cs +++ b/OpenRA.Mods.Cnc/Activities/Leap.cs @@ -94,8 +94,6 @@ namespace OpenRA.Mods.Cnc.Activities // We are at the destination if (++ticks >= length) { - mobile.IsMoving = false; - // Revoke the run condition attack.IsAiming = false; @@ -113,8 +111,6 @@ namespace OpenRA.Mods.Cnc.Activities return this; } - mobile.IsMoving = true; - return this; } diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithLandingCraftAnimation.cs b/OpenRA.Mods.Cnc/Traits/Render/WithLandingCraftAnimation.cs index 2ed30796e6..f4fb1eb77b 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithLandingCraftAnimation.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithLandingCraftAnimation.cs @@ -50,7 +50,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render public bool ShouldBeOpen() { - if (move.IsMoving || self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length > 0) + if (move.CurrentMovementTypes.HasFlag(MovementType.Horizontal) || self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length > 0) return false; return cargo.CurrentAdjacentCells.Any(c => self.World.Map.Contains(c) diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs b/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs index 4301f98bd2..f42fc29b05 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithVoxelWalkerBody.cs @@ -72,7 +72,8 @@ namespace OpenRA.Mods.Cnc.Traits.Render void ITick.Tick(Actor self) { - if (movement.IsMoving) + if (movement.CurrentMovementTypes.HasFlag(MovementType.Horizontal) + || movement.CurrentMovementTypes.HasFlag(MovementType.Turn)) tick++; if (tick < info.TickRate) diff --git a/OpenRA.Mods.Cnc/Traits/TDGunboat.cs b/OpenRA.Mods.Cnc/Traits/TDGunboat.cs index d346684a1a..c67bc39e82 100644 --- a/OpenRA.Mods.Cnc/Traits/TDGunboat.cs +++ b/OpenRA.Mods.Cnc/Traits/TDGunboat.cs @@ -201,9 +201,7 @@ namespace OpenRA.Mods.Cnc.Traits public CPos NearestMoveableCell(CPos cell) { return cell; } // Actors with TDGunboat always move - public bool IsMoving { get { return true; } set { } } - - public bool IsMovingVertically { get { return false; } set { } } + public MovementType CurrentMovementTypes { get { return MovementType.Horizontal; } set { } } public bool CanEnterTargetNow(Actor self, Target target) { diff --git a/OpenRA.Mods.Common/Activities/Move/Drag.cs b/OpenRA.Mods.Common/Activities/Move/Drag.cs index 5a913ebc7a..eaa21d1bd6 100644 --- a/OpenRA.Mods.Common/Activities/Move/Drag.cs +++ b/OpenRA.Mods.Common/Activities/Move/Drag.cs @@ -19,7 +19,6 @@ namespace OpenRA.Mods.Common.Activities public class Drag : Activity { readonly IPositionable positionable; - readonly IMove movement; readonly IDisabledTrait disableable; WPos start, end; int length; @@ -28,8 +27,7 @@ namespace OpenRA.Mods.Common.Activities public Drag(Actor self, WPos start, WPos end, int length) { positionable = self.Trait(); - movement = self.TraitOrDefault(); - disableable = movement as IDisabledTrait; + disableable = self.TraitOrDefault() as IDisabledTrait; this.start = start; this.end = end; this.length = length; @@ -47,15 +45,7 @@ namespace OpenRA.Mods.Common.Activities positionable.SetVisualPosition(self, pos); if (++ticks >= length) - { - if (movement != null) - movement.IsMoving = false; - return NextActivity; - } - - if (movement != null) - movement.IsMoving = true; return this; } diff --git a/OpenRA.Mods.Common/Activities/Move/Move.cs b/OpenRA.Mods.Common/Activities/Move/Move.cs index 1542160565..0d5ff56858 100644 --- a/OpenRA.Mods.Common/Activities/Move/Move.cs +++ b/OpenRA.Mods.Common/Activities/Move/Move.cs @@ -188,18 +188,7 @@ namespace OpenRA.Mods.Common.Activities { path.Add(nextCell.Value.First); - // If Mobile.Info.AlwaysConsiderTurnAsMove is true, we consider Turn as movement regardless of facing delta size. - // IsMoving is then set back to false in Turn.OnLastRun. - // This is needed for actors that want to display their movement animation during turns (walker units, for example). - mobile.IsMoving = mobile.Info.AlwaysConsiderTurnAsMove; - - // HACK: To fix visual hiccups on actors with move animations during "L-turn to next part of movement" transitions - // or invisible mini-turns (due to less sprite facings than internal facings), we set IsMoving to 'true' during Turn activity - // when the facing delta is low enough to be covered with a single Turn tick. - // To avoid issues, we also make these mini-turns uninterruptible (like MovePart activities) to ensure the actor - // finishes that mini-turn before starting something else. - var facingWithinTolerance = Util.FacingWithinTolerance(mobile.Facing, firstFacing, mobile.TurnSpeed); - QueueChild(self, new Turn(self, firstFacing, facingWithinTolerance, !facingWithinTolerance), true); + QueueChild(self, new Turn(self, firstFacing), true); return this; } @@ -337,7 +326,6 @@ namespace OpenRA.Mods.Common.Activities return this; var ret = InnerTick(self, Move.mobile); - Move.mobile.IsMoving = ret is MovePart; if (moveFraction > MoveFractionTotal) moveFraction = MoveFractionTotal; diff --git a/OpenRA.Mods.Common/Activities/Move/VisualMoveIntoTarget.cs b/OpenRA.Mods.Common/Activities/Move/VisualMoveIntoTarget.cs index 451785fa5f..443b791d4d 100644 --- a/OpenRA.Mods.Common/Activities/Move/VisualMoveIntoTarget.cs +++ b/OpenRA.Mods.Common/Activities/Move/VisualMoveIntoTarget.cs @@ -33,7 +33,6 @@ namespace OpenRA.Mods.Common.Activities protected override void OnFirstRun(Actor self) { targetStartPos = target.Positions.PositionClosestTo(self.CenterPosition); - mobile.IsMoving = true; } public override Activity Tick(Actor self) @@ -84,11 +83,6 @@ namespace OpenRA.Mods.Common.Activities return this; } - protected override void OnLastRun(Actor self) - { - mobile.IsMoving = false; - } - public override IEnumerable GetTargets(Actor self) { yield return target; diff --git a/OpenRA.Mods.Common/Activities/Turn.cs b/OpenRA.Mods.Common/Activities/Turn.cs index 3257cfd237..1035d541e7 100644 --- a/OpenRA.Mods.Common/Activities/Turn.cs +++ b/OpenRA.Mods.Common/Activities/Turn.cs @@ -19,32 +19,18 @@ namespace OpenRA.Mods.Common.Activities { readonly IDisabledTrait disablable; readonly IFacing facing; - readonly Mobile mobile; readonly int desiredFacing; - readonly bool setIsMoving; - public Turn(Actor self, int desiredFacing, bool setIsMoving = false, bool isInterruptible = true) + public Turn(Actor self, int desiredFacing) { disablable = self.TraitOrDefault() as IDisabledTrait; facing = self.Trait(); this.desiredFacing = desiredFacing; - this.setIsMoving = setIsMoving; - IsInterruptible = isInterruptible; - - // This might look confusing, but the current implementation of Mobile is both IMove and IDisabledTrait, - // and this way we can save a separate Mobile trait look-up. - mobile = disablable as Mobile; - } - - protected override void OnFirstRun(Actor self) - { - if (setIsMoving && mobile != null && !mobile.IsMoving) - mobile.IsMoving = true; } public override Activity Tick(Actor self) { - if (IsInterruptible && IsCanceling) + if (IsCanceling) return NextActivity; if (disablable != null && disablable.IsTraitDisabled) @@ -57,12 +43,5 @@ namespace OpenRA.Mods.Common.Activities return this; } - - protected override void OnLastRun(Actor self) - { - // If Mobile.IsMoving was set to 'true' earlier, we want to reset it to 'false' before the next tick. - if (setIsMoving && mobile != null && mobile.IsMoving) - mobile.IsMoving = false; - } } } diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index b3cc705132..17499bce81 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -176,6 +176,7 @@ namespace OpenRA.Mods.Common.Traits ConditionManager conditionManager; IDisposable reservation; IEnumerable speedModifiers; + INotifyMoving[] notifyMoving; [Sync] public int Facing { get; set; } [Sync] public WPos CenterPosition { get; private set; } @@ -191,9 +192,9 @@ namespace OpenRA.Mods.Common.Traits int airborneToken = ConditionManager.InvalidConditionToken; int cruisingToken = ConditionManager.InvalidConditionToken; - bool isMoving; - bool isMovingVertically; + MovementType movementTypes; WPos cachedPosition; + int cachedFacing; bool? landNow; public Aircraft(ActorInitializer init, AircraftInfo info) @@ -233,6 +234,7 @@ namespace OpenRA.Mods.Common.Traits conditionManager = self.TraitOrDefault(); speedModifiers = self.TraitsImplementing().ToArray().Select(sm => sm.GetSpeedModifier()); cachedPosition = self.CenterPosition; + notifyMoving = self.TraitsImplementing().ToArray(); } void INotifyAddedToWorld.AddedToWorld(Actor self) @@ -317,10 +319,23 @@ namespace OpenRA.Mods.Common.Traits } } + var oldCachedFacing = cachedFacing; + cachedFacing = Facing; + var oldCachedPosition = cachedPosition; cachedPosition = self.CenterPosition; - isMoving = (oldCachedPosition - cachedPosition).HorizontalLengthSquared != 0; - isMovingVertically = (oldCachedPosition - cachedPosition).VerticalLengthSquared != 0; + + var newMovementTypes = MovementType.None; + if (oldCachedFacing != Facing) + newMovementTypes |= MovementType.Turn; + + if ((oldCachedPosition - cachedPosition).HorizontalLengthSquared != 0) + newMovementTypes |= MovementType.Horizontal; + + if ((oldCachedPosition - cachedPosition).VerticalLengthSquared != 0) + newMovementTypes |= MovementType.Vertical; + + CurrentMovementTypes = newMovementTypes; Repulse(); } @@ -712,9 +727,22 @@ namespace OpenRA.Mods.Common.Traits public CPos NearestMoveableCell(CPos cell) { return cell; } - public bool IsMoving { get { return isMoving; } set { } } + public MovementType CurrentMovementTypes + { + get + { + return movementTypes; + } - public bool IsMovingVertically { get { return isMovingVertically; } set { } } + set + { + var oldValue = movementTypes; + movementTypes = value; + if (value != oldValue) + foreach (var n in notifyMoving) + n.MovementTypeChanged(self, value); + } + } public bool CanEnterTargetNow(Actor self, Target target) { diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnMovement.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnMovement.cs index 1d3befbe32..03b92eed73 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnMovement.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnMovement.cs @@ -50,8 +50,8 @@ namespace OpenRA.Mods.Common.Traits if (conditionManager == null) return; - var isMovingVertically = Info.ConsiderVerticalMovement ? movement.IsMovingVertically : false; - var isMoving = !IsTraitDisabled && !self.IsDead && (movement.IsMoving || isMovingVertically); + var isMovingVertically = Info.ConsiderVerticalMovement ? movement.CurrentMovementTypes.HasFlag(MovementType.Vertical); + var isMoving = !IsTraitDisabled && !self.IsDead && (movement.CurrentMovementTypes.HasFlag(MovementType.Horizontal) || isMovingVertically); if (isMoving && conditionToken == ConditionManager.InvalidConditionToken) conditionToken = conditionManager.GrantCondition(self, Info.Condition); else if (!isMoving && conditionToken != ConditionManager.InvalidConditionToken) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 34e3017026..6a3fe57b28 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -33,9 +33,6 @@ namespace OpenRA.Mods.Common.Traits [Desc("Speed at which the actor turns.")] public readonly int TurnSpeed = 255; - [Desc("Should turning always be considering as moving (instead of only turning while moving forward).")] - public readonly bool AlwaysConsiderTurnAsMove = false; - public readonly int Speed = 1; public readonly string Cursor = "move"; @@ -124,22 +121,39 @@ namespace OpenRA.Mods.Common.Traits } } - public class Mobile : PausableConditionalTrait, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, + public class Mobile : PausableConditionalTrait, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, ITick, IFacing, IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier, INotifyBecomingIdle { readonly Actor self; readonly Lazy> speedModifiers; - #region IMove IsMoving checks - public bool IsMoving { get; set; } - public bool IsMovingVertically { get { return false; } set { } } + #region IMove CurrentMovementTypes + MovementType movementTypes; + public MovementType CurrentMovementTypes + { + get + { + return movementTypes; + } + + set + { + var oldValue = movementTypes; + movementTypes = value; + if (value != oldValue) + foreach (var n in notifyMoving) + n.MovementTypeChanged(self, value); + } + } #endregion - int facing; + int oldFacing, facing; + WPos oldPos; CPos fromCell, toCell; public SubCell FromSubCell, ToSubCell; INotifyCustomLayerChanged[] notifyCustomLayerChanged; INotifyVisualPositionChanged[] notifyVisualPositionChanged; + INotifyMoving[] notifyMoving; INotifyFinishedMoving[] notifyFinishedMoving; #region IFacing @@ -201,11 +215,27 @@ namespace OpenRA.Mods.Common.Traits { notifyCustomLayerChanged = self.TraitsImplementing().ToArray(); notifyVisualPositionChanged = self.TraitsImplementing().ToArray(); + notifyMoving = self.TraitsImplementing().ToArray(); notifyFinishedMoving = self.TraitsImplementing().ToArray(); base.Created(self); } + void ITick.Tick(Actor self) + { + var newMovementTypes = MovementType.None; + if (oldPos != CenterPosition) + newMovementTypes |= MovementType.Horizontal; + + if (oldFacing != Facing) + newMovementTypes |= MovementType.Turn; + + CurrentMovementTypes = newMovementTypes; + + oldPos = CenterPosition; + oldFacing = Facing; + } + void INotifyAddedToWorld.AddedToWorld(Actor self) { self.World.AddToMaps(self, this); diff --git a/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs b/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs index 7bef9f55f9..4cb23dd5de 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs @@ -140,12 +140,12 @@ namespace OpenRA.Mods.Common.Traits.Render wasModifying = rsm.IsModifyingSequence; } - if ((state != AnimationState.Moving || dirty) && move.IsMoving) + if ((state != AnimationState.Moving || dirty) && move.CurrentMovementTypes.HasFlag(MovementType.Horizontal)) { state = AnimationState.Moving; DefaultAnimation.PlayRepeating(NormalizeInfantrySequence(self, Info.MoveSequence)); } - else if (((state == AnimationState.Moving || dirty) && !move.IsMoving) + else if (((state == AnimationState.Moving || dirty) && !move.CurrentMovementTypes.HasFlag(MovementType.Horizontal)) || ((state == AnimationState.Idle || state == AnimationState.IdleAnimating) && !self.IsIdle)) PlayStandAnimation(self); diff --git a/OpenRA.Mods.Common/Traits/Render/WithMoveAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithMoveAnimation.cs index 5f8ca47d1f..5f7b6468a3 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithMoveAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithMoveAnimation.cs @@ -51,7 +51,7 @@ namespace OpenRA.Mods.Common.Traits.Render if (IsTraitDisabled || wsb.IsTraitDisabled) return; - var isMoving = movement.IsMoving && !self.IsDead; + var isMoving = movement.CurrentMovementTypes.HasFlag(MovementType.Horizontal) && !self.IsDead; if (isMoving ^ (wsb.DefaultAnimation.CurrentSequence.Name != Info.MoveSequence)) return; diff --git a/OpenRA.Mods.Common/Traits/World/Locomotor.cs b/OpenRA.Mods.Common/Traits/World/Locomotor.cs index cf8234faf4..8b0be570eb 100644 --- a/OpenRA.Mods.Common/Traits/World/Locomotor.cs +++ b/OpenRA.Mods.Common/Traits/World/Locomotor.cs @@ -223,7 +223,7 @@ namespace OpenRA.Mods.Common.Traits static bool IsMovingInMyDirection(Actor self, Actor other) { var otherMobile = other.TraitOrDefault(); - if (otherMobile == null || !otherMobile.IsMoving) + if (otherMobile == null || !otherMobile.CurrentMovementTypes.HasFlag(MovementType.Horizontal)) return false; var selfMobile = self.TraitOrDefault(); diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 95661c3e81..ef6ef5811f 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -410,8 +410,7 @@ namespace OpenRA.Mods.Common.Traits Activity VisualMove(Actor self, WPos fromPos, WPos toPos); int EstimatedMoveDuration(Actor self, WPos fromPos, WPos toPos); CPos NearestMoveableCell(CPos target); - bool IsMoving { get; set; } - bool IsMovingVertically { get; set; } + MovementType CurrentMovementTypes { get; set; } bool CanEnterTargetNow(Actor self, Target target); } @@ -548,4 +547,19 @@ namespace OpenRA.Mods.Common.Traits { bool PreventMapSpawn(World world, ActorReference actorReference); } + + [Flags] + public enum MovementType + { + None = 0, + Horizontal = 1, + Vertical = 2, + Turn = 4 + } + + [RequireExplicitImplementation] + public interface INotifyMoving + { + void MovementTypeChanged(Actor self, MovementType type); + } }