diff --git a/OpenRA.Mods.Cnc/Traits/Attack/AttackPopupTurreted.cs b/OpenRA.Mods.Cnc/Traits/Attack/AttackPopupTurreted.cs index 7535057a20..dc800fd31d 100644 --- a/OpenRA.Mods.Cnc/Traits/Attack/AttackPopupTurreted.cs +++ b/OpenRA.Mods.Cnc/Traits/Attack/AttackPopupTurreted.cs @@ -76,19 +76,12 @@ namespace OpenRA.Mods.Cnc.Traits { state = PopupState.Closed; wsb.PlayCustomAnimationRepeating(self, info.ClosedIdleSequence); - turret.DesiredFacing = null; + turret.FaceTarget(self, Target.Invalid); } } protected override bool CanAttack(Actor self, Target target) { - if (state == PopupState.Transitioning) - return false; - - if (!base.CanAttack(self, target)) - return false; - - idleTicks = 0; if (state == PopupState.Closed) { state = PopupState.Transitioning; @@ -97,9 +90,14 @@ namespace OpenRA.Mods.Cnc.Traits state = PopupState.Open; wsb.PlayCustomAnimationRepeating(self, wsb.Info.Sequence); }); - return false; + + idleTicks = 0; } + if (state == PopupState.Transitioning || !base.CanAttack(self, target)) + return false; + + idleTicks = 0; return true; } @@ -107,17 +105,18 @@ namespace OpenRA.Mods.Cnc.Traits { if (state == PopupState.Open && idleTicks++ > info.CloseDelay) { - turret.DesiredFacing = info.DefaultFacing.Facing; + var facingOffset = new WVec(0, -1024, 0).Rotate(WRot.FromYaw(info.DefaultFacing)); + turret.FaceTarget(self, Target.FromPos(self.CenterPosition + facingOffset)); state = PopupState.Rotating; } - else if (state == PopupState.Rotating && turret.TurretFacing == info.DefaultFacing.Facing) + else if (state == PopupState.Rotating && turret.HasAchievedDesiredFacing) { state = PopupState.Transitioning; wsb.PlayCustomAnimation(self, info.ClosingSequence, () => { state = PopupState.Closed; wsb.PlayCustomAnimationRepeating(self, info.ClosedIdleSequence); - turret.DesiredFacing = null; + turret.FaceTarget(self, Target.Invalid); }); } } diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithEmbeddedTurretSpriteBody.cs b/OpenRA.Mods.Cnc/Traits/Render/WithEmbeddedTurretSpriteBody.cs index cbdbd84338..c6e273f1c6 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithEmbeddedTurretSpriteBody.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithEmbeddedTurretSpriteBody.cs @@ -34,7 +34,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render var wsb = init.Actor.TraitInfos().FirstOrDefault(); // Show the correct turret facing - var anim = new Animation(init.World, image, Turreted.TurretFacingFromInit(init, t)); + var anim = new Animation(init.World, image, t.WorldFacingFromInit(init)); anim.PlayRepeating(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), wsb.Sequence)); yield return new SpriteActorPreview(anim, () => WVec.Zero, () => 0, p, rs.Scale); @@ -49,7 +49,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render { // Turret artwork is baked into the sprite, so only the first turret makes sense. var turreted = self.TraitsImplementing().FirstOrDefault(); - return () => WAngle.FromFacing(turreted.TurretFacing); + return () => turreted.WorldOrientation.Yaw; } public WithEmbeddedTurretSpriteBody(ActorInitializer init, WithSpriteBodyInfo info) diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithGunboatBody.cs b/OpenRA.Mods.Cnc/Traits/Render/WithGunboatBody.cs index 199709ebe7..8d87214734 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithGunboatBody.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithGunboatBody.cs @@ -50,7 +50,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render { // Turret artwork is baked into the sprite, so only the first turret makes sense. var turreted = self.TraitsImplementing().FirstOrDefault(); - return () => WAngle.FromFacing(turreted.TurretFacing); + return () => turreted.WorldOrientation.Yaw; } public WithGunboatBody(ActorInitializer init, WithGunboatBodyInfo info) diff --git a/OpenRA.Mods.Common/Activities/DeliverUnit.cs b/OpenRA.Mods.Common/Activities/DeliverUnit.cs index d740cb984e..7992dc3bca 100644 --- a/OpenRA.Mods.Common/Activities/DeliverUnit.cs +++ b/OpenRA.Mods.Common/Activities/DeliverUnit.cs @@ -84,15 +84,7 @@ namespace OpenRA.Mods.Common.Activities var targetPosition = self.CenterPosition + body.LocalToWorld(localOffset); var targetLocation = self.World.Map.CellContaining(targetPosition); carryall.Carryable.Trait().SetPosition(carryall.Carryable, targetLocation, SubCell.FullCell); - - // HACK: directly manipulate the turret facings to match the new orientation - // This can eventually go away, when we make turret facings relative to the body - var carryableFacing = carryall.Carryable.Trait(); - var facingDelta = facing.Facing - carryableFacing.Facing; - foreach (var t in carryall.Carryable.TraitsImplementing()) - t.TurretFacing += facingDelta.Facing; - - carryableFacing.Facing = facing.Facing; + carryall.Carryable.Trait().Facing = facing.Facing; // Put back into world self.World.AddFrameEndTask(w => diff --git a/OpenRA.Mods.Common/Traits/Armament.cs b/OpenRA.Mods.Common/Traits/Armament.cs index 98ab3575a7..72357c7dd4 100644 --- a/OpenRA.Mods.Common/Traits/Armament.cs +++ b/OpenRA.Mods.Common/Traits/Armament.cs @@ -374,19 +374,18 @@ namespace OpenRA.Mods.Common.Traits protected virtual WVec CalculateMuzzleOffset(Actor self, Barrel b) { - var bodyOrientation = coords.QuantizeOrientation(self, self.Orientation); + // Weapon offset in turret coordinates var localOffset = b.Offset + new WVec(-Recoil, WDist.Zero, WDist.Zero); - if (turret != null) - { - // WorldOrientation is quantized to satisfy the *Fudges. - // Need to then convert back to a pseudo-local coordinate space, apply offsets, - // then rotate back at the end - var turretOrientation = turret.WorldOrientation(self) - bodyOrientation; - localOffset = localOffset.Rotate(turretOrientation); - localOffset += turret.Offset; - } - return coords.LocalToWorld(localOffset.Rotate(bodyOrientation)); + // Turret coordinates to body coordinates + var bodyOrientation = coords.QuantizeOrientation(self, self.Orientation); + if (turret != null) + localOffset = localOffset.Rotate(turret.WorldOrientation) + turret.Offset.Rotate(bodyOrientation); + else + localOffset = localOffset.Rotate(bodyOrientation); + + // Body coordinates to world coordinates + return coords.LocalToWorld(localOffset); } public WRot MuzzleOrientation(Actor self, Barrel b) @@ -396,10 +395,7 @@ namespace OpenRA.Mods.Common.Traits protected virtual WRot CalculateMuzzleOrientation(Actor self, Barrel b) { - var orientation = turret != null ? turret.WorldOrientation(self) : - coords.QuantizeOrientation(self, self.Orientation); - - return orientation + WRot.FromYaw(b.Yaw); + return WRot.FromYaw(b.Yaw).Rotate(turret != null ? turret.WorldOrientation : self.Orientation); } public Actor Actor { get { return self; } } diff --git a/OpenRA.Mods.Common/Traits/Cargo.cs b/OpenRA.Mods.Common/Traits/Cargo.cs index 38b86e1948..8c2a94e20a 100644 --- a/OpenRA.Mods.Common/Traits/Cargo.cs +++ b/OpenRA.Mods.Common/Traits/Cargo.cs @@ -371,9 +371,6 @@ namespace OpenRA.Mods.Common.Traits var passengerFacing = passenger.TraitOrDefault(); if (passengerFacing != null) passengerFacing.Facing = facing.Value.Facing + Info.PassengerFacing; - - foreach (var t in passenger.TraitsImplementing()) - t.TurretFacing = (facing.Value.Facing + Info.PassengerFacing).Facing; } public void Load(Actor self, Actor a) diff --git a/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs b/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs index 1070a5d5e7..a5fa9156fe 100644 --- a/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs +++ b/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs @@ -110,9 +110,15 @@ namespace OpenRA.Mods.Common.Traits foreach (var b in a.Barrels) { + var barrelEnd = new Barrel + { + Offset = b.Offset + new WVec(224, 0, 0), + Yaw = b.Yaw + }; + var muzzle = self.CenterPosition + a.MuzzleOffset(self, b); - var dirOffset = new WVec(0, -224, 0).Rotate(a.MuzzleOrientation(self, b)); - yield return new LineAnnotationRenderable(muzzle, muzzle + dirOffset, 1, Color.White); + var endMuzzle = self.CenterPosition + a.MuzzleOffset(self, barrelEnd); + yield return new LineAnnotationRenderable(muzzle, endMuzzle, 1, Color.White); } } } diff --git a/OpenRA.Mods.Common/Traits/HitShape.cs b/OpenRA.Mods.Common/Traits/HitShape.cs index 36c378c5f0..ce0034222b 100644 --- a/OpenRA.Mods.Common/Traits/HitShape.cs +++ b/OpenRA.Mods.Common/Traits/HitShape.cs @@ -110,11 +110,7 @@ namespace OpenRA.Mods.Common.Traits if (turret != null) { - // WorldOrientation is quantized to satisfy the *Fudges. - // Need to then convert back to a pseudo-local coordinate space, apply offsets, - // then rotate back at the end - var turretOrientation = turret.WorldOrientation(self) - quantizedBodyOrientation; - localOffset = localOffset.Rotate(turretOrientation); + localOffset = localOffset.Rotate(turret.LocalOrientation); localOffset += turret.Offset; } @@ -123,27 +119,15 @@ namespace OpenRA.Mods.Common.Traits public WDist DistanceFromEdge(Actor self, WPos pos) { - var origin = self.CenterPosition; - var orientation = self.Orientation; - if (turret != null) - { - origin += turret.Position(self); - orientation = turret.WorldOrientation(self); - } - + var origin = turret != null ? self.CenterPosition + turret.Position(self) : self.CenterPosition; + var orientation = turret != null ? turret.WorldOrientation : self.Orientation; return Info.Type.DistanceFromEdge(pos, origin, orientation); } public IEnumerable RenderDebugOverlay(Actor self, WorldRenderer wr) { - var origin = self.CenterPosition; - var orientation = self.Orientation; - if (turret != null) - { - origin += turret.Position(self); - orientation = turret.WorldOrientation(self); - } - + var origin = turret != null ? self.CenterPosition + turret.Position(self) : self.CenterPosition; + var orientation = turret != null ? turret.WorldOrientation : self.Orientation; return Info.Type.RenderDebugOverlay(wr, origin, orientation); } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithMuzzleOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithMuzzleOverlay.cs index 4603558811..45e033eafc 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithMuzzleOverlay.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithMuzzleOverlay.cs @@ -52,10 +52,8 @@ namespace OpenRA.Mods.Common.Traits.Render var turreted = self.TraitsImplementing() .FirstOrDefault(t => t.Name == arm.Info.Turret); - // Workaround for broken ternary operators in certain versions of mono (3.10 and - // certain versions of the 3.8 series): https://bugzilla.xamarin.com/show_bug.cgi?id=23319 if (turreted != null) - getFacing = () => WAngle.FromFacing(turreted.TurretFacing); + getFacing = () => turreted.WorldOrientation.Yaw; else if (facing != null) getFacing = () => facing.Facing; else diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteBarrel.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteBarrel.cs index 75580a34cd..a25ef5707c 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithSpriteBarrel.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteBarrel.cs @@ -45,7 +45,7 @@ namespace OpenRA.Mods.Common.Traits.Render var t = init.Actor.TraitInfos() .First(tt => tt.Turret == armament.Turret); - var turretFacing = Turreted.TurretFacingFromInit(init, t); + var turretFacing = t.WorldFacingFromInit(init); var anim = new Animation(init.World, image, turretFacing); anim.Play(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequence)); @@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Traits.Render .First(tt => tt.Name == armament.Info.Turret); rs = self.Trait(); - DefaultAnimation = new Animation(self.World, rs.GetImage(self), () => WAngle.FromFacing(turreted.TurretFacing)); + DefaultAnimation = new Animation(self.World, rs.GetImage(self), () => turreted.WorldOrientation.Yaw); DefaultAnimation.PlayRepeating(NormalizeSequence(self, Info.Sequence)); rs.Add(new AnimationWithOffset( DefaultAnimation, () => BarrelOffset(), () => IsTraitDisabled, p => RenderUtils.ZOffsetFromCenter(self, p, 0))); @@ -98,21 +98,10 @@ namespace OpenRA.Mods.Common.Traits.Render WVec BarrelOffset() { + var orientation = turreted != null ? turreted.WorldOrientation : self.Orientation; var localOffset = Info.LocalOffset + new WVec(-armament.Recoil, WDist.Zero, WDist.Zero); - var turretOffset = turreted != null ? turreted.Position(self) : WVec.Zero; - var quantizedBody = body.QuantizeOrientation(self, self.Orientation); - var turretOrientation = turreted != null ? turreted.WorldOrientation(self) - quantizedBody : WRot.None; - - var quantizedTurret = body.QuantizeOrientation(self, turretOrientation); - return turretOffset + body.LocalToWorld(localOffset.Rotate(quantizedTurret).Rotate(quantizedBody)); - } - - IEnumerable BarrelRotation() - { - var b = self.Orientation; - var qb = body.QuantizeOrientation(self, b); - yield return turreted.WorldOrientation(self) - qb + WRot.FromYaw(b.Yaw - qb.Yaw); - yield return qb; + var turretLocalOffset = turreted != null ? turreted.Offset : WVec.Zero; + return body.LocalToWorld(turretLocalOffset + localOffset.Rotate(orientation)); } } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs index 25112f1c38..cfaa8aac2e 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs @@ -50,7 +50,7 @@ namespace OpenRA.Mods.Common.Traits.Render var t = init.Actor.TraitInfos() .First(tt => tt.Turret == Turret); - var turretFacing = Turreted.TurretFacingFromInit(init, t); + var turretFacing = t.WorldFacingFromInit(init); var anim = new Animation(init.World, image, turretFacing); anim.Play(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequence)); @@ -90,7 +90,7 @@ namespace OpenRA.Mods.Common.Traits.Render arms = self.TraitsImplementing() .Where(w => w.Info.Turret == info.Turret).ToArray(); - DefaultAnimation = new Animation(self.World, rs.GetImage(self), () => WAngle.FromFacing(t.TurretFacing)); + DefaultAnimation = new Animation(self.World, rs.GetImage(self), () => t.WorldOrientation.Yaw); DefaultAnimation.PlayRepeating(NormalizeSequence(self, info.Sequence)); rs.Add(new AnimationWithOffset(DefaultAnimation, () => TurretOffset(self), @@ -106,10 +106,9 @@ namespace OpenRA.Mods.Common.Traits.Render if (!Info.Recoils) return t.Position(self); - var recoil = arms.Aggregate(WDist.Zero, (a, b) => a + b.Recoil); - var localOffset = new WVec(-recoil, WDist.Zero, WDist.Zero); - var quantizedWorldTurret = t.WorldOrientation(self); - return t.Position(self) + body.LocalToWorld(localOffset.Rotate(quantizedWorldTurret)); + var recoilDist = arms.Aggregate(WDist.Zero, (a, b) => a + b.Recoil); + var recoil = new WVec(-recoilDist, WDist.Zero, WDist.Zero); + return t.Position(self) + body.LocalToWorld(recoil.Rotate(t.WorldOrientation)); } public string NormalizeSequence(Actor self, string sequence) diff --git a/OpenRA.Mods.Common/Traits/Render/WithVoxelBarrel.cs b/OpenRA.Mods.Common/Traits/Render/WithVoxelBarrel.cs index 753dce7eb7..10e295adbb 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithVoxelBarrel.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithVoxelBarrel.cs @@ -51,13 +51,11 @@ namespace OpenRA.Mods.Common.Traits.Render var model = init.World.ModelCache.GetModelSequence(image, Sequence); - var turretFacing = Turreted.TurretFacingFromInit(init, t); - Func turretOrientation = () => body.QuantizeOrientation(WRot.FromYaw(turretFacing() - orientation().Yaw), facings); - - Func quantizedTurret = () => body.QuantizeOrientation(turretOrientation(), facings); + var turretOrientation = t.PreviewOrientation(init, orientation, facings); Func quantizedBody = () => body.QuantizeOrientation(orientation(), facings); - Func barrelOffset = () => body.LocalToWorld((t.Offset + LocalOffset.Rotate(quantizedTurret())).Rotate(quantizedBody())); - Func barrelOrientation = () => turretOrientation().Rotate(orientation()); + Func barrelOffset = () => body.LocalToWorld((t.Offset + LocalOffset.Rotate(turretOrientation())).Rotate(quantizedBody())); + Func barrelOrientation = () => LocalOrientation.Rotate(turretOrientation()).Rotate(quantizedBody()); + yield return new ModelAnimation(model, barrelOffset, barrelOrientation, () => false, () => 0, ShowShadow); } } @@ -87,21 +85,15 @@ namespace OpenRA.Mods.Common.Traits.Render WVec BarrelOffset() { - var b = self.Orientation; - var qb = body.QuantizeOrientation(self, b); + var orientation = turreted != null ? turreted.WorldOrientation : self.Orientation; var localOffset = Info.LocalOffset + new WVec(-armament.Recoil, WDist.Zero, WDist.Zero); var turretLocalOffset = turreted != null ? turreted.Offset : WVec.Zero; - var turretOrientation = turreted != null ? turreted.WorldOrientation(self) - b + WRot.FromYaw(b.Yaw - qb.Yaw) : WRot.None; - - return body.LocalToWorld((turretLocalOffset + localOffset.Rotate(turretOrientation)).Rotate(qb)); + return body.LocalToWorld(turretLocalOffset + localOffset.Rotate(orientation)); } WRot BarrelRotation() { - var b = self.Orientation; - var qb = body.QuantizeOrientation(self, b); - var t = turreted.WorldOrientation(self) - b + WRot.FromYaw(b.Yaw - qb.Yaw); - return Info.LocalOrientation.Rotate(t).Rotate(qb); + return Info.LocalOrientation.Rotate(turreted != null ? turreted.WorldOrientation : self.Orientation); } } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithVoxelTurret.cs b/OpenRA.Mods.Common/Traits/Render/WithVoxelTurret.cs index 318b061910..d753ed0480 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithVoxelTurret.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithVoxelTurret.cs @@ -37,17 +37,12 @@ namespace OpenRA.Mods.Common.Traits.Render if (!EnabledByDefault) yield break; - var body = init.Actor.TraitInfo(); var t = init.Actor.TraitInfos() .First(tt => tt.Turret == Turret); var model = init.World.ModelCache.GetModelSequence(image, Sequence); - Func turretOffset = () => body.LocalToWorld(t.Offset.Rotate(orientation())); - - var turretFacing = Turreted.TurretFacingFromInit(init, t); - Func turretOrientation = () => WRot.FromYaw(turretFacing() - orientation().Yaw) - .Rotate(body.QuantizeOrientation(orientation(), facings)); - + var turretOffset = t.PreviewPosition(init, orientation); + var turretOrientation = t.PreviewOrientation(init, orientation, facings); yield return new ModelAnimation(model, turretOffset, turretOrientation, () => false, () => 0, ShowShadow); } } @@ -68,15 +63,8 @@ namespace OpenRA.Mods.Common.Traits.Render var rv = self.Trait(); rv.Add(new ModelAnimation(self.World.ModelCache.GetModelSequence(rv.Image, Info.Sequence), - () => turreted.Position(self), TurretRotation, + () => turreted.Position(self), () => turreted.WorldOrientation, () => IsTraitDisabled, () => 0, info.ShowShadow)); } - - WRot TurretRotation() - { - var b = self.Orientation; - var qb = body.QuantizeOrientation(self, b); - return (turreted.WorldOrientation(self) - b + WRot.FromYaw(b.Yaw - qb.Yaw)).Rotate(qb); - } } } diff --git a/OpenRA.Mods.Common/Traits/ThrowsParticle.cs b/OpenRA.Mods.Common/Traits/ThrowsParticle.cs index 58fd931f66..f74b1b2a33 100644 --- a/OpenRA.Mods.Common/Traits/ThrowsParticle.cs +++ b/OpenRA.Mods.Common/Traits/ThrowsParticle.cs @@ -68,7 +68,7 @@ namespace OpenRA.Mods.Common.Traits // TODO: Carry orientation over from the parent instead of just facing var dynamicFacingInit = init.GetOrDefault(); var bodyFacing = dynamicFacingInit != null ? dynamicFacingInit.Value() : init.GetValue(WAngle.Zero); - facing = Turreted.TurretFacingFromInit(init, info, WAngle.Zero)(); + facing = TurretedInfo.WorldFacingFromInit(init, info, WAngle.Zero)(); // Calculate final position var throwRotation = WRot.FromYaw(new WAngle(Game.CosmeticRandom.Next(1024))); diff --git a/OpenRA.Mods.Common/Traits/Turreted.cs b/OpenRA.Mods.Common/Traits/Turreted.cs index 94e174aeb0..217aa255db 100644 --- a/OpenRA.Mods.Common/Traits/Turreted.cs +++ b/OpenRA.Mods.Common/Traits/Turreted.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.Mods.Common.Graphics; using OpenRA.Primitives; using OpenRA.Traits; @@ -54,36 +55,7 @@ namespace OpenRA.Mods.Common.Traits (actor, value) => actor.ReplaceInit(new TurretFacingInit(this, new WAngle((int)value)), this)); } - public override object Create(ActorInitializer init) { return new Turreted(init, this); } - } - - public class Turreted : PausableConditionalTrait, ITick, IDeathActorInitModifier, IActorPreviewInitModifier - { - AttackTurreted attack; - IFacing facing; - BodyOrientation body; - - [Sync] - public int QuantizedFacings = 0; - - [Sync] - public int TurretFacing = 0; - - public int? DesiredFacing; - int realignTick = 0; - - // For subclasses that want to move the turret relative to the body - protected WVec localOffset = WVec.Zero; - - public WVec Offset { get { return Info.Offset + localOffset; } } - public string Name { get { return Info.Turret; } } - - public static Func TurretFacingFromInit(IActorInitializer init, TurretedInfo info) - { - return TurretFacingFromInit(init, info, info.InitialFacing); - } - - public static Func TurretFacingFromInit(IActorInitializer init, TraitInfo info, WAngle defaultFacing) + public static Func WorldFacingFromInit(IActorInitializer init, TraitInfo info, WAngle defaultFacing) { // (Dynamic)TurretFacingInit is specified relative to the actor body. // We need to add the body facing to return an absolute world angle. @@ -102,17 +74,97 @@ namespace OpenRA.Mods.Common.Traits return bodyFacing != null ? (Func)(() => bodyFacing() + facing) : () => facing; } - var dynamicFacingInit = init.GetOrDefault(); + var dynamicFacingInit = init.GetOrDefault(info); if (dynamicFacingInit != null) return bodyFacing != null ? () => bodyFacing() + dynamicFacingInit.Value() : dynamicFacingInit.Value; - return bodyFacing ?? (Func)(() => defaultFacing); + return bodyFacing ?? (() => defaultFacing); } + public Func WorldFacingFromInit(IActorInitializer init) + { + return WorldFacingFromInit(init, this, InitialFacing); + } + + public Func LocalFacingFromInit(IActorInitializer init) + { + var turretFacingInit = init.GetOrDefault(this); + if (turretFacingInit != null) + { + var facing = turretFacingInit.Value; + return () => facing; + } + + var dynamicFacingInit = init.GetOrDefault(this); + if (dynamicFacingInit != null) + return dynamicFacingInit.Value; + + return () => InitialFacing; + } + + // Turret offset in world-space + public Func PreviewPosition(ActorPreviewInitializer init, Func orientation) + { + var body = init.Actor.TraitInfo(); + return () => body.LocalToWorld(Offset.Rotate(orientation())); + } + + // Orientation in world-space + public Func PreviewOrientation(ActorPreviewInitializer init, Func orientation, int facings) + { + var body = init.Actor.TraitInfo(); + var turretFacing = LocalFacingFromInit(init); + + Func world = () => WRot.FromYaw(turretFacing()).Rotate(orientation()); + if (facings == 0) + return world; + + // Quantize orientation to match a rendered sprite + // Implies no pitch or roll + return () => WRot.FromYaw(body.QuantizeFacing(world().Yaw, facings)); + } + + public override object Create(ActorInitializer init) { return new Turreted(init, this); } + } + + public class Turreted : PausableConditionalTrait, ITick, IDeathActorInitModifier, IActorPreviewInitModifier + { + AttackTurreted attack; + IFacing facing; + BodyOrientation body; + + [Sync] + public int QuantizedFacings = 0; + + WVec? desiredDirection = null; + int realignTick = 0; + + public WRot WorldOrientation + { + get + { + var world = facing != null ? LocalOrientation.Rotate(facing.Orientation) : LocalOrientation; + if (QuantizedFacings == 0) + return world; + + // Quantize orientation to match a rendered sprite + // Implies no pitch or roll + return WRot.FromYaw(body.QuantizeFacing(world.Yaw, QuantizedFacings)); + } + } + + public WRot LocalOrientation { get; private set; } + + // For subclasses that want to move the turret relative to the body + protected WVec localOffset = WVec.Zero; + + public WVec Offset { get { return Info.Offset + localOffset; } } + public string Name { get { return Info.Turret; } } + public Turreted(ActorInitializer init, TurretedInfo info) : base(info) { - TurretFacing = TurretFacingFromInit(init, Info)().Facing; + LocalOrientation = WRot.FromYaw(info.LocalFacingFromInit(init)()); } protected override void Created(Actor self) @@ -143,7 +195,7 @@ namespace OpenRA.Mods.Common.Traits if (realignTick < Info.RealignDelay) realignTick++; else if (Info.RealignDelay > -1) - DesiredFacing = null; + desiredDirection = null; MoveTurret(); } @@ -154,10 +206,33 @@ namespace OpenRA.Mods.Common.Traits } } + WAngle? DesiredLocalFacing + { + get + { + // A null value means we don't have a target + if (!desiredDirection.HasValue) + return null; + + // A zero value means that we have a target, but it is on top of us + if (desiredDirection.Value == WVec.Zero) + return LocalOrientation.Yaw; + + // PERF: If the turret rotation axis is vertical we can directly take the difference in facing/yaw + var o = facing != null ? facing.Orientation : (WRot?)null; + if (o == null || (o.Value.Pitch == WAngle.Zero && o.Value.Roll == WAngle.Zero)) + return o.HasValue ? desiredDirection.Value.Yaw - o.Value.Yaw : desiredDirection.Value.Yaw; + + // If the turret rotation axis is not vertical we must transform the + // target direction into the turrets local coordinate system + return desiredDirection.Value.Rotate(-o.Value).Yaw; + } + } + void MoveTurret() { - var df = DesiredFacing ?? (facing != null ? facing.Facing.Facing : TurretFacing); - TurretFacing = Util.TickFacing(TurretFacing, df, Info.TurnSpeed.Facing); + var desired = DesiredLocalFacing ?? Info.InitialFacing; + LocalOrientation = LocalOrientation.WithYaw(Util.TickFacing(LocalOrientation.Yaw, desired, Info.TurnSpeed)); } public bool FaceTarget(Actor self, Target target) @@ -165,17 +240,27 @@ namespace OpenRA.Mods.Common.Traits if (IsTraitDisabled || IsTraitPaused || attack == null || attack.IsTraitDisabled || attack.IsTraitPaused) return false; - var pos = self.CenterPosition; - var targetPos = attack.GetTargetPosition(pos, target); - var delta = targetPos - pos; - DesiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw.Facing : TurretFacing; + if (target.Type == TargetType.Invalid) + { + desiredDirection = null; + return false; + } + + var turretPos = self.CenterPosition + Position(self); + var targetPos = attack.GetTargetPosition(turretPos, target); + desiredDirection = targetPos - turretPos; + MoveTurret(); return HasAchievedDesiredFacing; } public virtual bool HasAchievedDesiredFacing { - get { return DesiredFacing == null || TurretFacing == DesiredFacing.Value; } + get + { + var desired = DesiredLocalFacing; + return !desired.HasValue || desired.Value == LocalOrientation.Yaw; + } } // Turret offset in world-space @@ -185,42 +270,14 @@ namespace OpenRA.Mods.Common.Traits return body.LocalToWorld(Offset.Rotate(bodyOrientation)); } - // Orientation in world-space - public WRot WorldOrientation(Actor self) - { - // Hack: turretFacing is relative to the world, so subtract the body yaw - var world = WRot.FromYaw(WAngle.FromFacing(TurretFacing)); - - if (QuantizedFacings == 0) - return world; - - // Quantize orientation to match a rendered sprite - // Implies no pitch or yaw - return WRot.FromYaw(body.QuantizeFacing(world.Yaw, QuantizedFacings)); - } - public void ModifyDeathActorInit(Actor self, TypeDictionary init) { - var turretFacing = WAngle.FromFacing(TurretFacing); - if (facing != null) - turretFacing -= facing.Facing; - - init.Add(new TurretFacingInit(Info, turretFacing)); + init.Add(new TurretFacingInit(Info, LocalOrientation.Yaw)); } void IActorPreviewInitModifier.ModifyActorPreviewInit(Actor self, TypeDictionary inits) { - Func bodyFacing = () => facing.Facing; - var dynamicFacing = inits.GetOrDefault(); - var staticFacing = inits.GetOrDefault(); - if (dynamicFacing != null) - bodyFacing = dynamicFacing.Value; - else if (staticFacing != null) - bodyFacing = () => staticFacing.Value; - - // Freeze the relative turret facing to its current value - var facingOffset = WAngle.FromFacing(TurretFacing) - bodyFacing(); - inits.Add(new DynamicTurretFacingInit(Info, () => bodyFacing() + facingOffset)); + inits.Add(new DynamicTurretFacingInit(Info, () => LocalOrientation.Yaw)); } protected override void TraitDisabled(Actor self)