diff --git a/OpenRa.Game/GameRules/UnitInfo.cs b/OpenRa.Game/GameRules/UnitInfo.cs index 10125f4007..03d7cb66ef 100755 --- a/OpenRa.Game/GameRules/UnitInfo.cs +++ b/OpenRa.Game/GameRules/UnitInfo.cs @@ -49,6 +49,7 @@ namespace OpenRa.Game.GameRules public readonly string[] BuiltAt = { }; public readonly int[] PrimaryOffset = { 0, 0 }; public readonly int[] SecondaryOffset = null; + public readonly int Recoil = 0; public UnitInfo(string name) { Name = name; } diff --git a/OpenRa.Game/Traits/AttackTurreted.cs b/OpenRa.Game/Traits/AttackTurreted.cs index 663da9c315..b422490a24 100755 --- a/OpenRa.Game/Traits/AttackTurreted.cs +++ b/OpenRa.Game/Traits/AttackTurreted.cs @@ -25,15 +25,29 @@ namespace OpenRa.Game.Traits } protected void DoAttack( Actor self ) - { - if( self.unitInfo.Primary != null && CheckFire( self, self.unitInfo.Primary, ref primaryFireDelay, self.unitInfo.PrimaryOffset ) ) + { + var rut = self.traits.GetOrDefault(); + + if( self.unitInfo.Primary != null && CheckFire( self, self.unitInfo.Primary, ref primaryFireDelay, + self.unitInfo.PrimaryOffset ) ) { - secondaryFireDelay = Math.Max( 4, secondaryFireDelay ); + secondaryFireDelay = Math.Max( 4, secondaryFireDelay ); + if (rut != null) rut.primaryRecoil = 1; return; + } + + if (self.unitInfo.Secondary != null && CheckFire(self, self.unitInfo.Secondary, ref secondaryFireDelay, + self.unitInfo.SecondaryOffset ?? self.unitInfo.PrimaryOffset)) + { + if (rut != null) + { + if (self.unitInfo.SecondaryOffset != null) + rut.secondaryRecoil = 1; + else + rut.primaryRecoil = 1; + } + return; } - if( self.unitInfo.Secondary != null && CheckFire( self, self.unitInfo.Secondary, ref secondaryFireDelay, - self.unitInfo.SecondaryOffset ?? self.unitInfo.PrimaryOffset) ) - return; } bool CheckFire( Actor self, string weaponName, ref int fireDelay, int[] offset ) @@ -45,7 +59,7 @@ namespace OpenRa.Game.Traits fireDelay = weapon.ROF; Game.world.Add( new Bullet( weaponName, self.Owner, self, - self.CenterLocation.ToInt2() + Util.GetTurretPosition( self, offset ), + self.CenterLocation.ToInt2() + Util.GetTurretPosition( self, offset, 0f ).ToInt2(), target.CenterLocation.ToInt2() ) ); return true; diff --git a/OpenRa.Game/Traits/RenderUnitTurreted.cs b/OpenRa.Game/Traits/RenderUnitTurreted.cs index 242df6f3e6..fea38cb4d5 100644 --- a/OpenRa.Game/Traits/RenderUnitTurreted.cs +++ b/OpenRa.Game/Traits/RenderUnitTurreted.cs @@ -10,6 +10,7 @@ namespace OpenRa.Game.Traits class RenderUnitTurreted : RenderUnit { public Animation turretAnim; + public float primaryRecoil = 0.0f, secondaryRecoil = 0.0f; public RenderUnitTurreted(Actor self) : base(self) @@ -24,16 +25,18 @@ namespace OpenRa.Game.Traits yield return Centered(anim.Image, self.CenterLocation); yield return Centered(turretAnim.Image, self.CenterLocation - + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset).ToFloat2()); + + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, primaryRecoil)); if (self.unitInfo.SecondaryOffset != null) yield return Centered(turretAnim.Image, self.CenterLocation - + Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset).ToFloat2()); + + Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, secondaryRecoil)); } public override void Tick(Actor self) { base.Tick(self); turretAnim.Tick(); + primaryRecoil = Math.Max(0f, primaryRecoil - .2f); + secondaryRecoil = Math.Max(0f, secondaryRecoil - .2f); } } } diff --git a/OpenRa.Game/Traits/Util.cs b/OpenRa.Game/Traits/Util.cs index e163db9cb4..34fe637be9 100755 --- a/OpenRa.Game/Traits/Util.cs +++ b/OpenRa.Game/Traits/Util.cs @@ -54,7 +54,30 @@ namespace OpenRa.Game.Traits return facing + turn; } - public static int2 GetTurretPosition(Actor self, int[] offset) + static float2 RotateVectorByFacing(float2 v, int facing, float ecc) + { + var angle = (facing / 256f) * (2 * (float)Math.PI); + var sinAngle = (float)Math.Sin(angle); + var cosAngle = (float)Math.Cos(angle); + + return new float2( + (cosAngle * v.X + sinAngle * v.Y), + ecc * (cosAngle * v.Y - sinAngle * v.X)); + } + + static float2 GetRecoil(Actor self, float recoil) + { + if (self.unitInfo.Recoil == 0) return float2.Zero; + var rut = self.traits.WithInterface().FirstOrDefault(); + if (rut == null) return float2.Zero; + + var facing = self.traits.Get().turretFacing; + var quantizedFacing = facing - facing % rut.turretAnim.CurrentSequence.Length; + + return RotateVectorByFacing(new float2(0, recoil * self.unitInfo.Recoil), quantizedFacing, .7f); + } + + public static float2 GetTurretPosition(Actor self, int[] offset, float recoil) { var ru = self.traits.WithInterface().FirstOrDefault(); if (ru == null) return int2.Zero; /* things that don't have a rotating base don't need the turrets repositioned */ @@ -62,16 +85,7 @@ namespace OpenRa.Game.Traits var bodyFacing = self.traits.Get().facing; var quantizedFacing = bodyFacing - bodyFacing % ru.anim.CurrentSequence.Length; - var angle = (quantizedFacing / 256f) * (2 * (float)Math.PI); - var sinAngle = Math.Sin(angle); - var cosAngle = Math.Cos(angle); - - var pos = new int2( - (int)(cosAngle * offset[0] + sinAngle * offset[1]), - (int)(cosAngle * offset[1] - sinAngle * offset[0])); - - pos.Y = (int)(.7f * pos.Y); - return pos; + return (RotateVectorByFacing(new float2(offset[0], offset[1]), quantizedFacing, .7f) + GetRecoil(self, recoil)); } } diff --git a/units.ini b/units.ini index dade7333aa..6033e558f0 100755 --- a/units.ini +++ b/units.ini @@ -19,12 +19,15 @@ Traits=Mobile, RenderUnit [1TNK] Description=Light Tank Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted +Recoil=2; [2TNK] Description=Medium Tank Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted +Recoil=3; [3TNK] Description=Heavy Tank Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted +Recoil=3; [4TNK] Description=Mammoth Tank Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted @@ -81,6 +84,7 @@ BuiltAt=syrd Traits=Mobile, Turreted, RenderUnitTurreted, AttackTurreted PrimaryOffset=0,17 SecondaryOffset=0,-17 +Recoil=3 [LST] Description=Transport WaterBound=yes