diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index a7c20c7781..dfcdd8cdca 100755 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -28,6 +28,8 @@ namespace OpenRA Lazy occupySpace; IHasLocation HasLocation; Lazy Move; + Lazy Facing; + public Cached Bounds; public Cached ExtendedBounds; @@ -44,7 +46,26 @@ namespace OpenRA return HasLocation.PxPosition; } } - + + public WPos CenterPosition + { + get + { + var altitude = Move.Value != null ? Move.Value.Altitude : 0; + return CenterLocation.ToWPos(altitude); + } + } + + public WRot Orientation + { + get + { + // TODO: Support non-zero pitch/roll in IFacing (IOrientation?) + var facing = Facing.Value != null ? Facing.Value.Facing : 0; + return new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facing)); + } + } + public Shroud.ActorVisibility Sight; [Sync] public Player Owner; @@ -74,6 +95,7 @@ namespace OpenRA } Move = Lazy.New(() => TraitOrDefault()); + Facing = Lazy.New(() => TraitOrDefault()); Size = Lazy.New(() => { diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index 830b80acb7..0b2270705c 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -211,5 +211,25 @@ namespace OpenRA.Graphics { palette.Update( world.WorldActor.TraitsImplementing() ); } + + // Conversion between world and screen coordinates + public float2 ScreenPosition(WPos pos) + { + var c = Game.CellSize/1024f; + return new float2(c*pos.X, c*(pos.Y - pos.Z)); + } + + public int2 ScreenPxPosition(WPos pos) + { + var c = Game.CellSize/1024f; + return new int2((int)(c*pos.X), (int)(c*(pos.Y - pos.Z))); + } + public float ScreenZOffset(WPos pos) { return pos.Z*Game.CellSize/1024f; } + + public float[] ScreenOffset(WVec vec) + { + var c = Game.CellSize/1024f; + return new float[] {c*vec.X, c*vec.Y, c*vec.Z}; + } } } diff --git a/OpenRA.Game/PPos.cs b/OpenRA.Game/PPos.cs index 77df86eb58..2703986419 100644 --- a/OpenRA.Game/PPos.cs +++ b/OpenRA.Game/PPos.cs @@ -23,6 +23,18 @@ namespace OpenRA public PPos(int x, int y) { X = x; Y = y; } public static readonly PPos Zero = new PPos(0, 0); + public static PPos FromWPos(WPos pos) + { + return new PPos(Game.CellSize*pos.X/1024, Game.CellSize*pos.Y/1024); + } + + // Temporary hack for things that throw away altitude and + // cache screen positions directly. This can go once all + // the callers understand world coordinates + public static PPos FromWPosHackZ(WPos pos) + { + return new PPos(Game.CellSize*pos.X/1024, Game.CellSize*(pos.Y - pos.Z)/1024); + } public static explicit operator PPos(int2 a) { return new PPos(a.X, a.Y); } @@ -74,6 +86,13 @@ namespace OpenRA Math.Min(r.Bottom, Math.Max(Y, r.Top))); } + public WPos ToWPos(int z) + { + return new WPos(1024*X/Game.CellSize, + 1024*Y/Game.CellSize, + 1024*z/Game.CellSize); + } + public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); } public override bool Equals(object obj) diff --git a/OpenRA.Game/Traits/Render/RenderSimple.cs b/OpenRA.Game/Traits/Render/RenderSimple.cs index d7927c6131..6b8b900204 100755 --- a/OpenRA.Game/Traits/Render/RenderSimple.cs +++ b/OpenRA.Game/Traits/Render/RenderSimple.cs @@ -27,6 +27,10 @@ namespace OpenRA.Traits [Desc("Change the sprite image size.")] public readonly float Scale = 1f; + [Desc("Number of facings for gameplay calculations. -1 indiciates auto-detection from sequence")] + public readonly int QuantizedFacings = -1; + + public readonly WAngle CameraPitch = WAngle.FromDegrees(40); public virtual object Create(ActorInitializer init) { return new RenderSimple(init.self); } public virtual IEnumerable RenderPreview(ActorInfo building, PaletteReference pr) @@ -38,7 +42,7 @@ namespace OpenRA.Traits } } - public class RenderSimple : IRender, IAutoSelectionSize, ITick, INotifyOwnerChanged + public class RenderSimple : IRender, ILocalCoordinatesModel, IAutoSelectionSize, ITick, INotifyOwnerChanged { public Dictionary anims = new Dictionary(); @@ -140,5 +144,22 @@ namespace OpenRA.Traits anim.PlayThen(NormalizeSequence(self, name), () => anim.PlayRepeating(NormalizeSequence(self, "idle"))); } + + public WVec LocalToWorld(WVec vec) + { + // RA's 2d perspective doesn't correspond to an orthonormal 3D + // coordinate system, so fudge the y axis to make things look good + return new WVec(vec.Y, -Info.CameraPitch.Sin()*vec.X/1024, vec.Z); + } + + public WRot QuantizeOrientation(Actor self, WRot orientation) + { + // Map yaw to the closest facing + var numDirs = Info.QuantizedFacings == -1 ? anim.CurrentSequence.Facings : Info.QuantizedFacings; + var facing = Util.QuantizeFacing(orientation.Yaw.Angle / 4, numDirs) * (256 / numDirs); + + // Roll and pitch are always zero + return new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facing)); + } } } diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 9b2cfa048c..6550938016 100755 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -211,6 +211,11 @@ namespace OpenRA.Traits public interface IPostRenderSelection { void RenderAfterWorld(WorldRenderer wr); } public interface IPreRenderSelection { void RenderBeforeWorld(WorldRenderer wr, Actor self); } public interface IRenderAsTerrain { IEnumerable RenderAsTerrain(WorldRenderer wr, Actor self); } + public interface ILocalCoordinatesModel + { + WVec LocalToWorld(WVec vec); + WRot QuantizeOrientation(Actor self, WRot orientation); + } public interface ITargetable {