diff --git a/OpenRA.Game/WAngle.cs b/OpenRA.Game/WAngle.cs index be63a238b7..4260b01149 100644 --- a/OpenRA.Game/WAngle.cs +++ b/OpenRA.Game/WAngle.cs @@ -64,6 +64,20 @@ namespace OpenRA return new WAngle(Angle - 512).Tan(); } + public static WAngle Lerp(WAngle a, WAngle b, int mul, int div) + { + // Map 1024 <-> 0 wrapping into linear space + var aa = a.Angle; + var bb = b.Angle; + if (aa > bb && aa - bb > 512) + aa -= 1024; + + if (bb > aa && bb - aa > 512) + bb -= 1024; + + return new WAngle(aa + (bb - aa) * mul / div); + } + public static WAngle ArcTan(int y, int x) { return ArcTan(y, x, 1); } public static WAngle ArcTan(int y, int x, int stride) { diff --git a/OpenRA.Mods.Common/Activities/Move/Move.cs b/OpenRA.Mods.Common/Activities/Move/Move.cs index 718e67c890..9b316fea46 100644 --- a/OpenRA.Mods.Common/Activities/Move/Move.cs +++ b/OpenRA.Mods.Common/Activities/Move/Move.cs @@ -292,6 +292,12 @@ namespace OpenRA.Mods.Common.Activities protected readonly Move Move; protected readonly WPos From, To; protected readonly int FromFacing, ToFacing; + protected readonly WPos ArcCenter; + protected readonly int ArcFromLength; + protected readonly WAngle ArcFromAngle; + protected readonly int ArcToLength; + protected readonly WAngle ArcToAngle; + protected readonly int MoveFractionTotal; protected int moveFraction; @@ -304,6 +310,24 @@ namespace OpenRA.Mods.Common.Activities ToFacing = toFacing; moveFraction = startingFraction; MoveFractionTotal = (to - from).Length; + + // Calculate an elliptical arc that joins from and to + if (fromFacing != toFacing) + { + // The center of rotation is where the normal vectors cross + var u = new WVec(1024, 0, 0).Rotate(WRot.FromFacing(fromFacing)); + var v = new WVec(1024, 0, 0).Rotate(WRot.FromFacing(toFacing)); + var w = from - to; + var s = (v.Y * w.X - v.X * w.Y) * 1024 / (v.X * u.Y - v.Y * u.X); + var x = from.X + s * u.X / 1024; + var y = from.Y + s * u.Y / 1024; + + ArcCenter = new WPos(x, y, 0); + ArcFromLength = (ArcCenter - from).HorizontalLength; + ArcFromAngle = (ArcCenter - from).Yaw; + ArcToLength = (ArcCenter - to).HorizontalLength; + ArcToAngle = (ArcCenter - to).Yaw; + } } public override void Cancel(Actor self) @@ -344,9 +368,22 @@ namespace OpenRA.Mods.Common.Activities void UpdateCenterLocation(Actor self, Mobile mobile) { - // avoid division through zero + // Avoid division through zero if (MoveFractionTotal != 0) - mobile.SetVisualPosition(self, WPos.Lerp(From, To, moveFraction, MoveFractionTotal)); + { + WPos pos; + if (FromFacing != ToFacing) + { + var angle = WAngle.Lerp(ArcFromAngle, ArcToAngle, moveFraction, MoveFractionTotal); + var length = int2.Lerp(ArcFromLength, ArcToLength, moveFraction, MoveFractionTotal); + var height = int2.Lerp(From.Z, To.Z, moveFraction, MoveFractionTotal); + pos = ArcCenter + new WVec(0, length, height).Rotate(WRot.FromYaw(angle)); + } + else + pos = WPos.Lerp(From, To, moveFraction, MoveFractionTotal); + + mobile.SetVisualPosition(self, pos); + } else mobile.SetVisualPosition(self, To);