diff --git a/OpenRa.Game/Effects/Missile.cs b/OpenRa.Game/Effects/Missile.cs index 6ea478c153..c0247567d1 100644 --- a/OpenRa.Game/Effects/Missile.cs +++ b/OpenRa.Game/Effects/Missile.cs @@ -45,7 +45,7 @@ namespace OpenRa.Game.Effects } const int MissileCloseEnough = 7; - const float Scale = .3f; + const float Scale = .2f; public void Tick() { @@ -71,8 +71,10 @@ namespace OpenRa.Game.Effects return; } - var speed = Weapon.Speed * ((targetAltitude > 0 && Weapon.TurboBoost) ? 1.5f : 1f); - var move = (Scale * speed / dist.Length) * dist; + var speed = Scale * Weapon.Speed * ((targetAltitude > 0 && Weapon.TurboBoost) ? 1.5f : 1f); + + var angle = Facing / 128f * Math.PI; + var move = speed * -float2.FromAngle((float)angle); Pos += move; if (Projectile.Animates) diff --git a/OpenRa.Game/GameRules/UnitInfo.cs b/OpenRa.Game/GameRules/UnitInfo.cs index 63acbe1870..7a29b23bb2 100755 --- a/OpenRa.Game/GameRules/UnitInfo.cs +++ b/OpenRa.Game/GameRules/UnitInfo.cs @@ -59,6 +59,10 @@ namespace OpenRa.Game.GameRules public readonly int UnloadFacing = 0; public readonly UnitMovementType[] PassengerTypes = null; + // weapon origins and firing angles within the turrets. 3 values per position. + public readonly int[] PrimaryLocalOffset = { }; + public readonly int[] SecondaryLocalOffset = { }; + public UnitInfo(string name) { Name = name; } } diff --git a/OpenRa.Game/Traits/AttackBase.cs b/OpenRa.Game/Traits/AttackBase.cs index d7b0328c58..411770437b 100644 --- a/OpenRa.Game/Traits/AttackBase.cs +++ b/OpenRa.Game/Traits/AttackBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using IjwFramework.Types; using OpenRa.Game.Effects; @@ -72,7 +73,7 @@ namespace OpenRa.Game.Traits var unit = self.traits.GetOrDefault(); if (self.Info.Primary != null && CheckFire(self, unit, self.Info.Primary, ref primaryFireDelay, - self.Info.PrimaryOffset, ref primaryBurst)) + self.Info.PrimaryOffset, ref primaryBurst, self.Info.PrimaryLocalOffset)) { secondaryFireDelay = Math.Max(4, secondaryFireDelay); primaryRecoil = 1; @@ -80,7 +81,7 @@ namespace OpenRa.Game.Traits } if (self.Info.Secondary != null && CheckFire(self, unit, self.Info.Secondary, ref secondaryFireDelay, - self.Info.SecondaryOffset ?? self.Info.PrimaryOffset, ref secondaryBurst)) + self.Info.SecondaryOffset ?? self.Info.PrimaryOffset, ref secondaryBurst, self.Info.SecondaryLocalOffset)) { if (self.Info.SecondaryOffset != null) secondaryRecoil = 1; else primaryRecoil = 1; @@ -88,7 +89,7 @@ namespace OpenRa.Game.Traits } } - bool CheckFire(Actor self, Unit unit, string weaponName, ref int fireDelay, int[] offset, ref int burst) + bool CheckFire(Actor self, Unit unit, string weaponName, ref int fireDelay, int[] offset, ref int burst, int[] localOffset) { if (fireDelay > 0) return false; @@ -101,6 +102,17 @@ namespace OpenRa.Game.Traits if (!Combat.WeaponValidForTarget(weapon, target)) return false; + var numOffsets = (localOffset.Length + 2) / 3; + if (numOffsets == 0) numOffsets = 1; + var localOffsetForShot = burst % numOffsets; + var thisLocalOffset = localOffset.Skip(3 * localOffsetForShot).Take(3).ToArray(); + + var fireOffset = new[] { + offset.ElementAtOrDefault(0) + thisLocalOffset.ElementAtOrDefault(0), + offset.ElementAtOrDefault(1) + thisLocalOffset.ElementAtOrDefault(1), + offset.ElementAtOrDefault(2), + offset.ElementAtOrDefault(3) }; + if (--burst > 0) fireDelay = 5; else @@ -109,7 +121,7 @@ namespace OpenRa.Game.Traits burst = weapon.Burst; } - var firePos = self.CenterLocation.ToInt2() + Util.GetTurretPosition(self, unit, offset, 0f).ToInt2(); + var firePos = self.CenterLocation.ToInt2() + Util.GetTurretPosition(self, unit, fireOffset, 0f).ToInt2(); var thisTarget = target; // closure. var destUnit = thisTarget.traits.GetOrDefault(); @@ -118,15 +130,17 @@ namespace OpenRa.Game.Traits var srcAltitude = unit != null ? unit.Altitude : 0; var destAltitude = destUnit != null ? destUnit.Altitude : 0; - var fireFacing = self.traits.Contains() - ? self.traits.Get().turretFacing : unit.Facing; - if( weapon.RenderAsTesla ) Game.world.Add( new TeslaZap( firePos, thisTarget.CenterLocation.ToInt2() ) ); - if( Rules.ProjectileInfo[ weapon.Projectile ].ROT != 0 ) + if (Rules.ProjectileInfo[weapon.Projectile].ROT != 0) + { + var fireFacing = thisLocalOffset.ElementAtOrDefault(2) + + (self.traits.Contains() ? self.traits.Get().turretFacing : unit.Facing); + Game.world.Add(new Missile(weaponName, self.Owner, self, firePos, thisTarget, srcAltitude, fireFacing)); + } else Game.world.Add(new Bullet(weaponName, self.Owner, self, firePos, thisTarget.CenterLocation.ToInt2(), srcAltitude, destAltitude)); diff --git a/OpenRa.Game/Traits/RenderSimple.cs b/OpenRa.Game/Traits/RenderSimple.cs index 48f9ec7837..d5db307c09 100644 --- a/OpenRa.Game/Traits/RenderSimple.cs +++ b/OpenRa.Game/Traits/RenderSimple.cs @@ -33,6 +33,7 @@ namespace OpenRa.Game.Traits public Animation Animation; public Func OffsetFunc; public Func DisableFunc; + public int ZOffset; public AnimationWithOffset( Animation a ) : this( a, null, null ) @@ -48,10 +49,9 @@ namespace OpenRa.Game.Traits public Renderable Image( Actor self ) { - if( OffsetFunc != null ) - return Util.Centered( self, Animation.Image, self.CenterLocation + OffsetFunc() ); - else - return Util.Centered( self, Animation.Image, self.CenterLocation ); + var r = Util.Centered( self, Animation.Image, self.CenterLocation + + (OffsetFunc != null ? OffsetFunc() : float2.Zero) ); + return ZOffset != 0 ? r.WithZOffset(ZOffset) : r; } public static implicit operator AnimationWithOffset( Animation a ) diff --git a/OpenRa.Game/Traits/RenderUnitTurreted.cs b/OpenRa.Game/Traits/RenderUnitTurreted.cs index c06614f4bd..f05caa57f4 100644 --- a/OpenRa.Game/Traits/RenderUnitTurreted.cs +++ b/OpenRa.Game/Traits/RenderUnitTurreted.cs @@ -19,16 +19,16 @@ namespace OpenRa.Game.Traits turretAnim.PlayFacing( "turret", () => turreted.turretFacing ); if( self.Info.PrimaryOffset != null ) - anims.Add( "turret_1", new AnimationWithOffset( + anims.Add("turret_1", new AnimationWithOffset( turretAnim, - () => Util.GetTurretPosition( self, unit, self.Info.PrimaryOffset, attack.primaryRecoil ), - null ) ); + () => Util.GetTurretPosition(self, unit, self.Info.PrimaryOffset, attack.primaryRecoil), + null) { ZOffset = 1 }); if( self.Info.SecondaryOffset != null ) - anims.Add( "turret_2", new AnimationWithOffset( + anims.Add("turret_2", new AnimationWithOffset( turretAnim, - () => Util.GetTurretPosition( self, unit, self.Info.SecondaryOffset, attack.secondaryRecoil ), - null ) ); + () => Util.GetTurretPosition(self, unit, self.Info.SecondaryOffset, attack.secondaryRecoil), + null) { ZOffset = 1 }); if( self.Info.MuzzleFlash ) { diff --git a/units.ini b/units.ini index 6ed539a5f0..5c39efaabf 100644 --- a/units.ini +++ b/units.ini @@ -42,6 +42,9 @@ Description=Mammoth Tank Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger, IronCurtainable Voice=VehicleVoice LongDesc=Big and slow tank, with anti-air capability.\n Strong vs Tanks, Aircraft\n Weak vs Infantry +PrimaryLocalOffset=-4,-5,0,4,-5,0 +SecondaryLocalOffset=-7,2,25,7,2,-25 +Recoil=4 [ARTY] Description=Artillery Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget, Repairable, Chronoshiftable, Passenger, IronCurtainable