1 #region Copyright & License Information
2 /*
3  * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
4  * This file is part of OpenRA, which is free software. It is made
5  * available to you under the terms of the GNU General Public License
6  * as published by the Free Software Foundation, either version 3 of
7  * the License, or (at your option) any later version. For more
8  * information, see COPYING.
9  */
10 #endregion
11 
12 using System;
13 using OpenRA.Mods.Common.Traits.Render;
14 using OpenRA.Traits;
15 
16 namespace OpenRA.Mods.Common.Traits
17 {
18 	public class BodyOrientationInfo : ITraitInfo
19 	{
20 		[Desc("Number of facings for gameplay calculations. -1 indicates auto-detection from another trait")]
21 		public readonly int QuantizedFacings = -1;
22 
23 		[Desc("Camera pitch for rotation calculations")]
24 		public readonly WAngle CameraPitch = WAngle.FromDegrees(40);
25 
26 		[Desc("Fudge the coordinate system angles like the early games.")]
27 		public readonly bool UseClassicPerspectiveFudge = true;
28 
29 		[Desc("Fudge the coordinate system angles like the early games.")]
30 		public readonly bool UseClassicFacingFudge = false;
31 
LocalToWorld(WVec vec)32 		public WVec LocalToWorld(WVec vec)
33 		{
34 			// Rotate by 90 degrees
35 			if (!UseClassicPerspectiveFudge)
36 				return new WVec(vec.Y, -vec.X, vec.Z);
37 
38 			// RA's 2d perspective doesn't correspond to an orthonormal 3D
39 			// coordinate system, so fudge the y axis to make things look good
40 			return new WVec(vec.Y, -CameraPitch.Sin() * vec.X / 1024, vec.Z);
41 		}
42 
QuantizeOrientation(WRot orientation, int facings)43 		public WRot QuantizeOrientation(WRot orientation, int facings)
44 		{
45 			// Quantization disabled
46 			if (facings == 0)
47 				return orientation;
48 
49 			// Map yaw to the closest facing
50 			var facing = QuantizeFacing(orientation.Yaw.Angle / 4, facings);
51 
52 			// Roll and pitch are always zero if yaw is quantized
53 			return new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facing));
54 		}
55 
QuantizeFacing(int facing, int facings)56 		public int QuantizeFacing(int facing, int facings)
57 		{
58 			return Util.QuantizeFacing(facing, facings, UseClassicFacingFudge) * (256 / facings);
59 		}
60 
Create(ActorInitializer init)61 		public object Create(ActorInitializer init) { return new BodyOrientation(init, this); }
62 	}
63 
64 	public class BodyOrientation : ISync
65 	{
66 		readonly BodyOrientationInfo info;
67 		readonly Lazy<int> quantizedFacings;
68 
69 		[Sync]
70 		public int QuantizedFacings { get { return quantizedFacings.Value; } }
71 
BodyOrientation(ActorInitializer init, BodyOrientationInfo info)72 		public BodyOrientation(ActorInitializer init, BodyOrientationInfo info)
73 		{
74 			this.info = info;
75 			var self = init.Self;
76 			var faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : self.Owner.Faction.InternalName;
77 
78 			quantizedFacings = Exts.Lazy(() =>
79 			{
80 				// Override value is set
81 				if (info.QuantizedFacings >= 0)
82 					return info.QuantizedFacings;
83 
84 				var qboi = self.Info.TraitInfoOrDefault<IQuantizeBodyOrientationInfo>();
85 
86 				// If a sprite actor has neither custom QuantizedFacings nor a trait implementing IQuantizeBodyOrientationInfo, throw
87 				if (qboi == null)
88 				{
89 					if (self.Info.HasTraitInfo<WithSpriteBodyInfo>())
90 						throw new InvalidOperationException("Actor '" + self.Info.Name + "' has a sprite body but no facing quantization."
91 							+ " Either add the QuantizeFacingsFromSequence trait or set custom QuantizedFacings on BodyOrientation.");
92 					else
93 						throw new InvalidOperationException("Actor type '" + self.Info.Name + "' does not define a quantized body orientation.");
94 				}
95 
96 				return qboi.QuantizedBodyFacings(self.Info, self.World.Map.Rules.Sequences, faction);
97 			});
98 		}
99 
100 		public WAngle CameraPitch { get { return info.CameraPitch; } }
101 
LocalToWorld(WVec vec)102 		public WVec LocalToWorld(WVec vec)
103 		{
104 			return info.LocalToWorld(vec);
105 		}
106 
QuantizeOrientation(Actor self, WRot orientation)107 		public WRot QuantizeOrientation(Actor self, WRot orientation)
108 		{
109 			return info.QuantizeOrientation(orientation, quantizedFacings.Value);
110 		}
111 
QuantizeFacing(int facing)112 		public int QuantizeFacing(int facing)
113 		{
114 			return info.QuantizeFacing(facing, quantizedFacings.Value);
115 		}
116 
QuantizeFacing(int facing, int facings)117 		public int QuantizeFacing(int facing, int facings)
118 		{
119 			return info.QuantizeFacing(facing, facings);
120 		}
121 	}
122 }
123