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