From 4c239d4ebc38429b33b453370bd91ba672f23287 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sun, 10 Jun 2018 00:24:51 +0200 Subject: [PATCH] Set Mobile.IsMoving to "true" if queueing a turn that takes only a single tick At the end of L-turns, actors often end up with an internal facing not 100% matching the direction of the next cell on their path. As a result, if they haven't reached their destination yet, Move queues a quick Turn as ChildActivity, which previously was not considered as IsMoving. However, we don't want those mini-turns to interrupt move animations, so we now consider them a move as well. Additionally, to avoid any issues, we make these mini-turns non-interruptible, just like the MovePart activities already are. --- OpenRA.Mods.Common/Activities/Move/Move.cs | 9 ++++++++- OpenRA.Mods.Common/Activities/Turn.cs | 22 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/OpenRA.Mods.Common/Activities/Move/Move.cs b/OpenRA.Mods.Common/Activities/Move/Move.cs index 7f7bda3552..33d8b6b579 100644 --- a/OpenRA.Mods.Common/Activities/Move/Move.cs +++ b/OpenRA.Mods.Common/Activities/Move/Move.cs @@ -224,7 +224,14 @@ namespace OpenRA.Mods.Common.Activities if (firstFacing != mobile.Facing) { path.Add(nextCell.Value.First); - QueueChild(new Turn(self, firstFacing)); + + // 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(new Turn(self, firstFacing, facingWithinTolerance, !facingWithinTolerance)); ChildActivity = ActivityUtils.RunActivity(self, ChildActivity); return this; } diff --git a/OpenRA.Mods.Common/Activities/Turn.cs b/OpenRA.Mods.Common/Activities/Turn.cs index d175ca9d0a..334b514d42 100644 --- a/OpenRA.Mods.Common/Activities/Turn.cs +++ b/OpenRA.Mods.Common/Activities/Turn.cs @@ -19,14 +19,27 @@ 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 isInterruptible = true) + public Turn(Actor self, int desiredFacing, bool setIsMoving = false, bool isInterruptible = true) { 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) @@ -44,5 +57,12 @@ 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 (mobile != null && mobile.IsMoving) + mobile.IsMoving = false; + } } }