Merge pull request #2770 from pchote/turret-cleanup
Weapon / Turret refactoring
This commit is contained in:
@@ -200,9 +200,10 @@ namespace OpenRA.Mods.RA.AI
|
||||
return RelativeValue(own, enemy, 100, SumOfValues<AttackBase>, (Actor a) =>
|
||||
{
|
||||
int sumOfDamage = 0;
|
||||
foreach (var weap in a.Trait<AttackBase>().Weapons)
|
||||
if (weap.Info.Warheads[0] != null)
|
||||
sumOfDamage += weap.Info.Warheads[0].Damage;
|
||||
var arms = a.TraitsImplementing<Armament>();
|
||||
foreach (var arm in arms)
|
||||
if (arm.Weapon.Warheads[0] != null)
|
||||
sumOfDamage += arm.Weapon.Warheads[0].Damage;
|
||||
return sumOfDamage;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -230,12 +230,13 @@ namespace OpenRA.Mods.RA.AI
|
||||
if (!target.HasTrait<TargetableUnit<TargetableUnitInfo>>() &&
|
||||
!target.HasTrait<TargetableBuilding>()) return false;
|
||||
|
||||
foreach (var weap in a.Trait<AttackBase>().Weapons)
|
||||
var arms = a.TraitsImplementing<Armament>();
|
||||
foreach (var arm in arms)
|
||||
if (target.HasTrait<TargetableUnit<TargetableUnitInfo>>() &&
|
||||
weap.Info.ValidTargets.Intersect(target.Trait<TargetableUnit<TargetableUnitInfo>>().TargetTypes) != null)
|
||||
arm.Weapon.ValidTargets.Intersect(target.Trait<TargetableUnit<TargetableUnitInfo>>().TargetTypes) != null)
|
||||
return true;
|
||||
else if (target.HasTrait<TargetableBuilding>() &&
|
||||
weap.Info.ValidTargets.Intersect(target.Trait<TargetableBuilding>().TargetTypes) != null)
|
||||
arm.Weapon.ValidTargets.Intersect(target.Trait<TargetableBuilding>().TargetTypes) != null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -253,12 +254,15 @@ namespace OpenRA.Mods.RA.AI
|
||||
foreach (var unit in units)
|
||||
if (unit != null && unit.HasTrait<AttackBase>() && !unit.HasTrait<Aircraft>()
|
||||
&& !unit.IsDisabled())
|
||||
foreach (var weap in unit.Trait<AttackBase>().Weapons)
|
||||
if (weap.Info.ValidTargets.Contains("Air"))
|
||||
{
|
||||
var arms = unit.TraitsImplementing<Armament>();
|
||||
foreach (var a in arms)
|
||||
if (a.Weapon.ValidTargets.Contains("Air"))
|
||||
{
|
||||
missileUnitsCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return missileUnitsCount;
|
||||
}
|
||||
|
||||
|
||||
198
OpenRA.Mods.RA/Armament.cs
Executable file
198
OpenRA.Mods.RA/Armament.cs
Executable file
@@ -0,0 +1,198 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Mods.RA.Render;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class Barrel
|
||||
{
|
||||
public PVecInt TurretSpaceOffset; // position in turret space
|
||||
public PVecInt ScreenSpaceOffset; // screen-space hack to make things line up good.
|
||||
public int Facing; // deviation from turret facing
|
||||
}
|
||||
|
||||
public class ArmamentInfo : ITraitInfo, Requires<AttackBaseInfo>
|
||||
{
|
||||
[WeaponReference]
|
||||
[Desc("Has to be defined here and in weapons.yaml.")]
|
||||
public readonly string Weapon = null;
|
||||
public readonly string Turret = "primary";
|
||||
public readonly int Recoil = 0;
|
||||
public readonly int FireDelay = 0;
|
||||
|
||||
public readonly float RecoilRecovery = 0.2f;
|
||||
public readonly int[] LocalOffset = { };
|
||||
|
||||
public object Create(ActorInitializer init) { return new Armament(init.self, this); }
|
||||
}
|
||||
|
||||
public class Armament : ITick
|
||||
{
|
||||
public readonly ArmamentInfo Info;
|
||||
public readonly WeaponInfo Weapon;
|
||||
public readonly Barrel[] Barrels;
|
||||
Lazy<Turreted> Turret;
|
||||
|
||||
public float Recoil { get; private set; }
|
||||
public int FireDelay { get; private set; }
|
||||
public int Burst { get; private set; }
|
||||
|
||||
public Armament(Actor self, ArmamentInfo info)
|
||||
{
|
||||
Info = info;
|
||||
|
||||
// We can't soft-depend on TraitInfo, so we have to wait
|
||||
// until runtime to cache this
|
||||
Turret = Lazy.New(() => self.TraitsImplementing<Turreted>().FirstOrDefault(t => t.info.Turret == info.Turret));
|
||||
|
||||
Weapon = Rules.Weapons[info.Weapon.ToLowerInvariant()];
|
||||
Burst = Weapon.Burst;
|
||||
|
||||
var barrels = new List<Barrel>();
|
||||
for (var i = 0; i < info.LocalOffset.Length / 5; i++)
|
||||
barrels.Add(new Barrel
|
||||
{
|
||||
TurretSpaceOffset = new PVecInt(info.LocalOffset[5 * i], info.LocalOffset[5 * i + 1]),
|
||||
ScreenSpaceOffset = new PVecInt(info.LocalOffset[5 * i + 2], info.LocalOffset[5 * i + 3]),
|
||||
Facing = info.LocalOffset[5 * i + 4],
|
||||
});
|
||||
|
||||
// if no barrels specified, the default is "turret position; turret facing".
|
||||
if (barrels.Count == 0)
|
||||
barrels.Add(new Barrel { TurretSpaceOffset = PVecInt.Zero, ScreenSpaceOffset = PVecInt.Zero, Facing = 0 });
|
||||
|
||||
Barrels = barrels.ToArray();
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (FireDelay > 0)
|
||||
--FireDelay;
|
||||
Recoil = Math.Max(0f, Recoil - Info.RecoilRecovery);
|
||||
}
|
||||
|
||||
public void CheckFire(Actor self, AttackBase attack, IMove move, IFacing facing, Target target)
|
||||
{
|
||||
if (FireDelay > 0) return;
|
||||
|
||||
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();
|
||||
if (limitedAmmo != null && !limitedAmmo.HasAmmo())
|
||||
return;
|
||||
|
||||
if (!Combat.IsInRange(self.CenterLocation, Weapon.Range, target)) return;
|
||||
if (Combat.IsInRange(self.CenterLocation, Weapon.MinRange, target)) return;
|
||||
if (!IsValidAgainst(self.World, target)) return;
|
||||
|
||||
var barrel = Barrels[Burst % Barrels.Length];
|
||||
var destMove = target.IsActor ? target.Actor.TraitOrDefault<IMove>() : null;
|
||||
|
||||
var args = new ProjectileArgs
|
||||
{
|
||||
weapon = Weapon,
|
||||
firedBy = self,
|
||||
target = target,
|
||||
|
||||
src = (self.CenterLocation + (PVecInt)MuzzlePxPosition(self, facing, barrel).ToInt2()),
|
||||
srcAltitude = move != null ? move.Altitude : 0,
|
||||
dest = target.CenterLocation,
|
||||
destAltitude = destMove != null ? destMove.Altitude : 0,
|
||||
|
||||
facing = barrel.Facing +
|
||||
(Turret.Value != null ? Turret.Value.turretFacing :
|
||||
facing != null ? facing.Facing : Util.GetFacing(target.CenterLocation - self.CenterLocation, 0)),
|
||||
|
||||
firepowerModifier = self.TraitsImplementing<IFirepowerModifier>()
|
||||
.Select(a => a.GetFirepowerModifier())
|
||||
.Product()
|
||||
};
|
||||
|
||||
attack.ScheduleDelayedAction(Info.FireDelay, () =>
|
||||
{
|
||||
if (args.weapon.Projectile != null)
|
||||
{
|
||||
var projectile = args.weapon.Projectile.Create(args);
|
||||
if (projectile != null)
|
||||
self.World.Add(projectile);
|
||||
|
||||
if (args.weapon.Report != null && args.weapon.Report.Any())
|
||||
Sound.Play(args.weapon.Report.Random(self.World.SharedRandom) + ".aud", self.CenterLocation);
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var na in self.TraitsImplementing<INotifyAttack>())
|
||||
na.Attacking(self, target);
|
||||
|
||||
Recoil = Info.Recoil;
|
||||
|
||||
if (--Burst > 0)
|
||||
FireDelay = Weapon.BurstDelay;
|
||||
else
|
||||
{
|
||||
FireDelay = Weapon.ROF;
|
||||
Burst = Weapon.Burst;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(World world, Target target)
|
||||
{
|
||||
if (target.IsActor)
|
||||
return Combat.WeaponValidForTarget(Weapon, target.Actor);
|
||||
else
|
||||
return Combat.WeaponValidForTarget(Weapon, world, target.CenterLocation.ToCPos());
|
||||
}
|
||||
|
||||
public bool IsReloading { get { return FireDelay > 0; } }
|
||||
|
||||
PVecFloat GetUnitspaceBarrelOffset(Actor self, IFacing facing, Barrel b)
|
||||
{
|
||||
if (Turret.Value == null && facing == null)
|
||||
return PVecFloat.Zero;
|
||||
|
||||
var turretFacing = Turret.Value != null ? Turret.Value.turretFacing : facing.Facing;
|
||||
return (PVecFloat)Util.RotateVectorByFacing(b.TurretSpaceOffset.ToFloat2(), turretFacing, .7f);
|
||||
}
|
||||
|
||||
public PVecFloat MuzzlePxPosition(Actor self, IFacing facing, Barrel b)
|
||||
{
|
||||
PVecFloat pos = b.ScreenSpaceOffset;
|
||||
|
||||
// local facing offset doesn't make sense for actors that don't rotate
|
||||
if (Turret.Value == null && facing == null)
|
||||
return pos;
|
||||
|
||||
if (Turret.Value != null)
|
||||
pos += Turret.Value.PxPosition(self, facing);
|
||||
|
||||
// Add local unitspace/turretspace offset
|
||||
var f = Turret.Value != null ? Turret.Value.turretFacing : facing.Facing;
|
||||
|
||||
// This is going away, so no point adding unnecessary usings
|
||||
var ru = self.TraitOrDefault<RenderUnit>();
|
||||
var numDirs = (ru != null) ? ru.anim.CurrentSequence.Facings : 8;
|
||||
var quantizedFacing = Util.QuantizeFacing(f, numDirs) * (256 / numDirs);
|
||||
|
||||
pos += (PVecFloat)Util.RotateVectorByFacing(b.TurretSpaceOffset.ToFloat2(), quantizedFacing, .7f);
|
||||
return pos;
|
||||
}
|
||||
|
||||
public PVecFloat RecoilPxOffset(Actor self, int facing)
|
||||
{
|
||||
var localRecoil = new float2(0, Recoil);
|
||||
return (PVecFloat)Util.RotateVectorByFacing(localRecoil, facing, .7f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,70 +19,34 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
public abstract class AttackBaseInfo : ITraitInfo
|
||||
{
|
||||
[WeaponReference]
|
||||
[Desc("Has to be defined here and in weapons.yaml.")]
|
||||
public readonly string PrimaryWeapon = null;
|
||||
[WeaponReference]
|
||||
public readonly string SecondaryWeapon = null;
|
||||
public readonly int PrimaryRecoil = 0;
|
||||
public readonly int SecondaryRecoil = 0;
|
||||
public readonly float PrimaryRecoilRecovery = 0.2f;
|
||||
public readonly float SecondaryRecoilRecovery = 0.2f;
|
||||
public readonly int[] PrimaryLocalOffset = { };
|
||||
public readonly int[] SecondaryLocalOffset = { };
|
||||
public readonly int[] PrimaryOffset = { 0, 0 };
|
||||
public readonly int[] SecondaryOffset = null;
|
||||
public readonly int FireDelay = 0;
|
||||
|
||||
public readonly bool AlignIdleTurrets = false;
|
||||
public readonly bool CanAttackGround = true;
|
||||
|
||||
public readonly int MinimumScanTimeInterval = 30;
|
||||
public readonly int MaximumScanTimeInterval = 60;
|
||||
|
||||
public abstract object Create(ActorInitializer init);
|
||||
|
||||
public float GetMaximumRange()
|
||||
{
|
||||
var priRange = PrimaryWeapon != null ? Rules.Weapons[PrimaryWeapon.ToLowerInvariant()].Range : 0;
|
||||
var secRange = SecondaryWeapon != null ? Rules.Weapons[SecondaryWeapon.ToLowerInvariant()].Range : 0;
|
||||
|
||||
return Math.Max(priRange, secRange);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AttackBase : IIssueOrder, IResolveOrder, ITick, IExplodeModifier, IOrderVoice, ISync
|
||||
{
|
||||
[Sync] public bool IsAttacking { get; internal set; }
|
||||
|
||||
public List<Weapon> Weapons = new List<Weapon>();
|
||||
public List<Turret> Turrets = new List<Turret>();
|
||||
|
||||
readonly Actor self;
|
||||
|
||||
Lazy<IEnumerable<Armament>> armaments;
|
||||
protected IEnumerable<Armament> Armaments { get { return armaments.Value; } }
|
||||
|
||||
public AttackBase(Actor self)
|
||||
{
|
||||
this.self = self;
|
||||
var info = self.Info.Traits.Get<AttackBaseInfo>();
|
||||
|
||||
Turrets.Add(new Turret(info.PrimaryOffset, info.PrimaryRecoilRecovery));
|
||||
if (info.SecondaryOffset != null)
|
||||
Turrets.Add(new Turret(info.SecondaryOffset, info.SecondaryRecoilRecovery));
|
||||
|
||||
if (info.PrimaryWeapon != null)
|
||||
Weapons.Add(new Weapon(info.PrimaryWeapon,
|
||||
Turrets[0], info.PrimaryLocalOffset, info.PrimaryRecoil));
|
||||
|
||||
if (info.SecondaryWeapon != null)
|
||||
Weapons.Add(new Weapon(info.SecondaryWeapon,
|
||||
info.SecondaryOffset != null ? Turrets[1] : Turrets[0], info.SecondaryLocalOffset, info.SecondaryRecoil));
|
||||
armaments = Lazy.New(() => self.TraitsImplementing<Armament>());
|
||||
}
|
||||
|
||||
protected virtual bool CanAttack(Actor self, Target target)
|
||||
{
|
||||
if (!self.IsInWorld) return false;
|
||||
if (!target.IsValid) return false;
|
||||
if (Weapons.All(w => w.IsReloading)) return false;
|
||||
if (Armaments.All(a => a.IsReloading)) return false;
|
||||
if (self.IsDisabled()) return false;
|
||||
|
||||
if (target.IsActor && target.Actor.HasTrait<ITargetable>() &&
|
||||
@@ -94,15 +58,12 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public bool ShouldExplode(Actor self) { return !IsReloading(); }
|
||||
|
||||
public bool IsReloading() { return Weapons.Any(w => w.IsReloading); }
|
||||
public bool IsReloading() { return Armaments.Any(a => a.IsReloading); }
|
||||
|
||||
List<Pair<int, Action>> delayedActions = new List<Pair<int, Action>>();
|
||||
|
||||
public virtual void Tick(Actor self)
|
||||
{
|
||||
foreach (var w in Weapons)
|
||||
w.Tick();
|
||||
|
||||
for (var i = 0; i < delayedActions.Count; i++)
|
||||
{
|
||||
var x = delayedActions[i];
|
||||
@@ -127,20 +88,20 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
var move = self.TraitOrDefault<IMove>();
|
||||
var facing = self.TraitOrDefault<IFacing>();
|
||||
foreach (var w in Weapons)
|
||||
w.CheckFire(self, this, move, facing, target);
|
||||
foreach (var a in Armaments)
|
||||
a.CheckFire(self, this, move, facing, target);
|
||||
}
|
||||
|
||||
public virtual int FireDelay( Actor self, Target target, AttackBaseInfo info )
|
||||
{
|
||||
return info.FireDelay;
|
||||
}
|
||||
|
||||
bool IsHeal { get { return Weapons[ 0 ].Info.Warheads[ 0 ].Damage < 0; } }
|
||||
|
||||
public IEnumerable<IOrderTargeter> Orders
|
||||
{
|
||||
get { yield return new AttackOrderTargeter( "Attack", 6, IsHeal ); }
|
||||
get
|
||||
{
|
||||
if (Armaments.Count() == 0)
|
||||
yield break;
|
||||
|
||||
bool isHeal = Armaments.First().Weapon.Warheads[0].Damage < 0;
|
||||
yield return new AttackOrderTargeter("Attack", 6, isHeal);
|
||||
}
|
||||
}
|
||||
|
||||
public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued )
|
||||
@@ -163,12 +124,6 @@ namespace OpenRA.Mods.RA
|
||||
self.SetTargetLine(target, Color.Red);
|
||||
AttackTarget(target, order.Queued, order.OrderString == "Attack");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* hack */
|
||||
if (self.HasTrait<Turreted>() && self.Info.Traits.Get<AttackBaseInfo>().AlignIdleTurrets)
|
||||
self.Trait<Turreted>().desiredFacing = null;
|
||||
}
|
||||
}
|
||||
|
||||
public string VoicePhraseForOrder(Actor self, Order order)
|
||||
@@ -178,10 +133,10 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public abstract Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove);
|
||||
|
||||
public bool HasAnyValidWeapons(Target t) { return Weapons.Any(w => w.IsValidAgainst(self.World, t)); }
|
||||
public float GetMaximumRange() { return Weapons.Max(w => w.Info.Range); }
|
||||
public bool HasAnyValidWeapons(Target t) { return Armaments.Any(a => a.IsValidAgainst(self.World, t)); }
|
||||
public float GetMaximumRange() { return Armaments.Select(a => a.Weapon.Range).Aggregate(0f, Math.Max); }
|
||||
|
||||
public Weapon ChooseWeaponForTarget(Target t) { return Weapons.FirstOrDefault(w => w.IsValidAgainst(self.World, t)); }
|
||||
public Armament ChooseArmamentForTarget(Target t) { return Armaments.FirstOrDefault(a => a.IsValidAgainst(self.World, t)); }
|
||||
|
||||
public void AttackTarget( Target target, bool queued, bool allowMove )
|
||||
{
|
||||
|
||||
@@ -42,10 +42,10 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
var weapon = ChooseWeaponForTarget(newTarget);
|
||||
if( weapon == null )
|
||||
var weapon = ChooseArmamentForTarget(newTarget);
|
||||
if (weapon == null)
|
||||
return null;
|
||||
return new Activities.Attack(newTarget, Math.Max(0, (int)weapon.Info.Range), allowMove);
|
||||
return new Activities.Attack(newTarget, Math.Max(0, (int)weapon.Weapon.Range), allowMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.RA.Activities;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -28,10 +29,15 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public override void DoAttack(Actor self, Target target)
|
||||
{
|
||||
if( !CanAttack( self, target ) ) return;
|
||||
if (!CanAttack(self, target))
|
||||
return;
|
||||
|
||||
var weapon = Weapons[0].Info;
|
||||
if( !Combat.IsInRange( self.CenterLocation, weapon.Range, target ) ) return;
|
||||
var a = ChooseArmamentForTarget(target);
|
||||
if (a == null)
|
||||
return;
|
||||
|
||||
if (!Combat.IsInRange(self.CenterLocation, a.Weapon.Range, target))
|
||||
return;
|
||||
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(new Leap(self, target));
|
||||
@@ -39,10 +45,10 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
var weapon = ChooseWeaponForTarget(newTarget);
|
||||
if( weapon == null )
|
||||
var a = ChooseArmamentForTarget(newTarget);
|
||||
if (a == null)
|
||||
return null;
|
||||
return new Activities.Attack(newTarget, Math.Max(0, (int)weapon.Info.Range), allowMove);
|
||||
return new Activities.Attack(newTarget, Math.Max(0, (int)a.Weapon.Range), allowMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Mods.RA.Activities;
|
||||
|
||||
@@ -26,15 +27,19 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public override void DoAttack(Actor self, Target target)
|
||||
{
|
||||
if (!CanAttack (self, target)) return;
|
||||
if (!CanAttack(self, target)) return;
|
||||
|
||||
var weapon = Weapons[0].Info;
|
||||
if (!Combat.IsInRange(self.CenterLocation, weapon.Range, target)) return;
|
||||
var arm = Armaments.FirstOrDefault();
|
||||
if (arm == null)
|
||||
return;
|
||||
|
||||
if (!Combat.IsInRange(self.CenterLocation, arm.Weapon.Range, target))
|
||||
return;
|
||||
|
||||
var move = self.TraitOrDefault<IMove>();
|
||||
var facing = self.TraitOrDefault<IFacing>();
|
||||
foreach (var w in Weapons)
|
||||
w.CheckFire(self, this, move, facing, target);
|
||||
foreach (var a in Armaments)
|
||||
a.CheckFire(self, this, move, facing, target);
|
||||
|
||||
if (target.Actor != null)
|
||||
target.Actor.ChangeOwner(self.Owner);
|
||||
|
||||
@@ -29,10 +29,10 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
var weapon = ChooseWeaponForTarget(newTarget);
|
||||
if( weapon == null )
|
||||
var weapon = ChooseArmamentForTarget(newTarget);
|
||||
if (weapon == null)
|
||||
return null;
|
||||
return new Activities.Heal(newTarget, Math.Max(0, (int)weapon.Info.Range), allowMove);
|
||||
return new Activities.Heal(newTarget, Math.Max(0, (int)weapon.Weapon.Range), allowMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Mods.RA.Buildings;
|
||||
using OpenRA.Mods.RA.Render;
|
||||
@@ -30,11 +31,13 @@ namespace OpenRA.Mods.RA
|
||||
AttackPopupTurretedInfo Info;
|
||||
int IdleTicks = 0;
|
||||
PopupState State = PopupState.Open;
|
||||
Turreted turret;
|
||||
|
||||
public AttackPopupTurreted(ActorInitializer init, AttackPopupTurretedInfo info) : base(init.self)
|
||||
{
|
||||
Info = info;
|
||||
buildComplete = init.Contains<SkipMakeAnimsInit>();
|
||||
turret = turrets.FirstOrDefault();
|
||||
}
|
||||
|
||||
protected override bool CanAttack( Actor self, Target target )
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.RA.Activities;
|
||||
using OpenRA.Mods.RA.Buildings;
|
||||
@@ -25,12 +26,12 @@ namespace OpenRA.Mods.RA
|
||||
class AttackTurreted : AttackBase, INotifyBuildComplete, ISync
|
||||
{
|
||||
protected Target target;
|
||||
protected Turreted turret;
|
||||
protected IEnumerable<Turreted> turrets;
|
||||
[Sync] protected bool buildComplete;
|
||||
|
||||
public AttackTurreted(Actor self) : base(self)
|
||||
{
|
||||
turret = self.Trait<Turreted>();
|
||||
turrets = self.TraitsImplementing<Turreted>();
|
||||
}
|
||||
|
||||
protected override bool CanAttack( Actor self, Target target )
|
||||
@@ -39,7 +40,12 @@ namespace OpenRA.Mods.RA
|
||||
return false;
|
||||
|
||||
if (!target.IsValid) return false;
|
||||
if (!turret.FaceTarget(self, target)) return false;
|
||||
|
||||
bool canAttack = false;
|
||||
foreach (var t in turrets)
|
||||
if (t.FaceTarget(self, target))
|
||||
canAttack = true;
|
||||
if (!canAttack) return false;
|
||||
|
||||
return base.CanAttack( self, target );
|
||||
}
|
||||
@@ -85,7 +91,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
var attack = self.Trait<AttackTurreted>();
|
||||
const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */
|
||||
var weapon = attack.ChooseWeaponForTarget(target);
|
||||
var weapon = attack.ChooseArmamentForTarget(target);
|
||||
|
||||
if (weapon != null)
|
||||
{
|
||||
@@ -93,7 +99,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
if (allowMove && self.HasTrait<Mobile>() && !self.Info.Traits.Get<MobileInfo>().OnRails)
|
||||
return Util.SequenceActivities(
|
||||
new Follow( target, Math.Max( 0, (int)weapon.Info.Range - RangeTolerance ) ),
|
||||
new Follow( target, Math.Max( 0, (int)weapon.Weapon.Range - RangeTolerance ) ),
|
||||
this );
|
||||
}
|
||||
|
||||
|
||||
@@ -208,48 +208,6 @@ namespace OpenRA.Mods.RA
|
||||
return false;
|
||||
}
|
||||
|
||||
static PVecFloat GetRecoil(Actor self, float recoil)
|
||||
{
|
||||
if (!self.HasTrait<RenderUnitTurreted>())
|
||||
return PVecFloat.Zero;
|
||||
|
||||
var facing = self.Trait<Turreted>().turretFacing;
|
||||
var localRecoil = new float2(0, recoil); // vector in turret-space.
|
||||
|
||||
return (PVecFloat)Util.RotateVectorByFacing(localRecoil, facing, .7f);
|
||||
}
|
||||
|
||||
public static PVecFloat GetTurretPosition(Actor self, IFacing facing, Turret turret)
|
||||
{
|
||||
if (facing == null) return turret.ScreenSpacePosition; /* things that don't have a rotating base don't need the turrets repositioned */
|
||||
|
||||
var ru = self.TraitOrDefault<RenderUnit>();
|
||||
var numDirs = (ru != null) ? ru.anim.CurrentSequence.Facings : 8;
|
||||
var bodyFacing = facing.Facing;
|
||||
var quantizedFacing = Util.QuantizeFacing(bodyFacing, numDirs) * (256 / numDirs);
|
||||
|
||||
return (PVecFloat)Util.RotateVectorByFacing(turret.UnitSpacePosition.ToFloat2(), quantizedFacing, .7f)
|
||||
+ GetRecoil(self, turret.Recoil)
|
||||
+ (PVecFloat)turret.ScreenSpacePosition.ToFloat2();
|
||||
}
|
||||
|
||||
static PVecFloat GetUnitspaceBarrelOffset(Actor self, IFacing facing, Turret turret, Barrel barrel)
|
||||
{
|
||||
var turreted = self.TraitOrDefault<Turreted>();
|
||||
if (turreted == null && facing == null)
|
||||
return PVecFloat.Zero;
|
||||
|
||||
var turretFacing = turreted != null ? turreted.turretFacing : facing.Facing;
|
||||
return (PVecFloat)Util.RotateVectorByFacing(barrel.TurretSpaceOffset.ToFloat2(), turretFacing, .7f);
|
||||
}
|
||||
|
||||
// gets the screen-space position of a barrel.
|
||||
public static PVecFloat GetBarrelPosition(Actor self, IFacing facing, Turret turret, Barrel barrel)
|
||||
{
|
||||
return GetTurretPosition(self, facing, turret) + barrel.ScreenSpaceOffset
|
||||
+ GetUnitspaceBarrelOffset(self, facing, turret, barrel);
|
||||
}
|
||||
|
||||
public static bool IsInRange( PPos attackOrigin, float range, Actor target )
|
||||
{
|
||||
var rsq = range * range * Game.CellSize * Game.CellSize;
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
history.Tick(self.CenterLocation - new PVecInt(0, move.Altitude) - (PVecInt)Combat.GetTurretPosition(self, facing, contrailTurret).ToInt2());
|
||||
history.Tick(self.CenterLocation - new PVecInt(0, move.Altitude) - (PVecInt)contrailTurret.PxPosition(self, facing).ToInt2());
|
||||
}
|
||||
|
||||
public void RenderAfterWorld(WorldRenderer wr, Actor self) { history.Render(self); }
|
||||
|
||||
@@ -55,7 +55,9 @@ namespace OpenRA.Mods.RA
|
||||
if (facing != null)
|
||||
td.Add(new FacingInit( facing.Facing ));
|
||||
|
||||
var turreted = self.TraitOrDefault<Turreted>();
|
||||
// TODO: This will only take the first turret if there are multiple
|
||||
// This isn't a problem with the current units, but may be a problem for mods
|
||||
var turreted = self.TraitsImplementing<Turreted>().FirstOrDefault();
|
||||
if (turreted != null)
|
||||
td.Add( new TurretFacingInit(turreted.turretFacing) );
|
||||
|
||||
|
||||
@@ -368,7 +368,6 @@
|
||||
<Compile Include="Turreted.cs" />
|
||||
<Compile Include="Valued.cs" />
|
||||
<Compile Include="WaterPaletteRotation.cs" />
|
||||
<Compile Include="Weapon.cs" />
|
||||
<Compile Include="Widgets\BuildPaletteWidget.cs" />
|
||||
<Compile Include="Widgets\Logic\ModBrowserLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\ColorPickerLogic.cs" />
|
||||
@@ -420,6 +419,7 @@
|
||||
<Compile Include="Widgets\ColorPreviewManagerWidget.cs" />
|
||||
<Compile Include="FogPalette.cs" />
|
||||
<Compile Include="Infiltrates.cs" />
|
||||
<Compile Include="Armament.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
|
||||
@@ -24,19 +24,17 @@ namespace OpenRA.Mods.RA.Render
|
||||
public RenderBuildingSeparateTurret(ActorInitializer init, RenderBuildingInfo info)
|
||||
: base(init, info)
|
||||
{
|
||||
var turreted = init.self.Trait<Turreted>();
|
||||
var attack = init.self.Trait<AttackBase>();
|
||||
var self = init.self;
|
||||
var turreted = self.TraitsImplementing<Turreted>();
|
||||
|
||||
var turretAnim = new Animation(GetImage(init.self), () => turreted.turretFacing);
|
||||
turretAnim.Play("turret");
|
||||
|
||||
for( var i = 0; i < attack.Turrets.Count; i++ )
|
||||
var i = 0;
|
||||
foreach (var t in turreted)
|
||||
{
|
||||
var turret = attack.Turrets[i];
|
||||
anims.Add( "turret_{0}".F(i),
|
||||
new AnimationWithOffset(turretAnim,
|
||||
() => Combat.GetTurretPosition(init.self, null, turret).ToFloat2(),
|
||||
null));
|
||||
var anim = new Animation(GetImage(self), () => t.turretFacing);
|
||||
anim.Play("turret");
|
||||
|
||||
anims.Add("turret_{0}".F(i++), new AnimationWithOffset(anim,
|
||||
() => t.PxPosition(self, null).ToFloat2(), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.RA.Buildings;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -26,7 +27,8 @@ namespace OpenRA.Mods.RA.Render
|
||||
|
||||
static Func<int> MakeTurretFacingFunc(Actor self)
|
||||
{
|
||||
var turreted = self.Trait<Turreted>();
|
||||
// Turret artwork is baked into the sprite, so only the first turret makes sense.
|
||||
var turreted = self.TraitsImplementing<Turreted>().FirstOrDefault();
|
||||
return () => turreted.turretFacing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,10 @@ namespace OpenRA.Mods.RA.Render
|
||||
|
||||
spinnerAnim.PlayRepeating("spinner");
|
||||
|
||||
var turret = new Turret(info.Offset);
|
||||
anims.Add("spinner", new AnimationWithOffset(
|
||||
spinnerAnim,
|
||||
() => Combat.GetTurretPosition( self, facing, new Turret(info.Offset)).ToFloat2(),
|
||||
() => turret.PxPosition(self, facing).ToFloat2(),
|
||||
null ) { ZOffset = 1 } );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -24,20 +26,30 @@ namespace OpenRA.Mods.RA.Render
|
||||
: base(self)
|
||||
{
|
||||
var facing = self.Trait<IFacing>();
|
||||
var turreted = self.Trait<Turreted>();
|
||||
var attack = self.Trait<AttackBase>();
|
||||
var turreted = self.TraitsImplementing<Turreted>();
|
||||
|
||||
var turretAnim = new Animation(GetImage(self), () => turreted.turretFacing );
|
||||
turretAnim.Play( "turret" );
|
||||
|
||||
for( var i = 0; i < attack.Turrets.Count; i++ )
|
||||
var i = 0;
|
||||
foreach (var t in turreted)
|
||||
{
|
||||
var turret = attack.Turrets[i];
|
||||
anims.Add( "turret_{0}".F(i),
|
||||
new AnimationWithOffset( turretAnim,
|
||||
() => Combat.GetTurretPosition( self, facing, turret ).ToFloat2(),
|
||||
null));
|
||||
var turret = t;
|
||||
|
||||
var anim = new Animation(GetImage(self), () => turret.turretFacing);
|
||||
anim.Play("turret");
|
||||
|
||||
anims.Add("turret_{0}".F(i++), new AnimationWithOffset(anim,
|
||||
() => turret.PxPosition(self, facing).ToFloat2() + RecoilOffset(self, turret), null));
|
||||
}
|
||||
}
|
||||
|
||||
float2 RecoilOffset(Actor self, Turreted t)
|
||||
{
|
||||
var a = self.TraitsImplementing<Armament>()
|
||||
.OrderByDescending(w => w.Recoil)
|
||||
.FirstOrDefault(w => w.Info.Turret == t.info.Turret);
|
||||
if (a == null)
|
||||
return float2.Zero;
|
||||
|
||||
return a.RecoilPxOffset(self, t.turretFacing).ToFloat2();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
using System;
|
||||
using OpenRA.Mods.RA;
|
||||
|
||||
namespace OpenRA.Mods.RA.Render
|
||||
{
|
||||
@@ -27,25 +29,25 @@ namespace OpenRA.Mods.RA.Render
|
||||
|
||||
public WithMuzzleFlash(Actor self)
|
||||
{
|
||||
var attack = self.Trait<AttackBase>();
|
||||
var render = self.Trait<RenderSimple>();
|
||||
var facing = self.TraitOrDefault<IFacing>();
|
||||
var turreted = self.TraitOrDefault<Turreted>();
|
||||
var getFacing = turreted != null ? () => turreted.turretFacing :
|
||||
facing != null ? (Func<int>)(() => facing.Facing) : () => 0;
|
||||
|
||||
foreach (var w in attack.Weapons)
|
||||
foreach( var b in w.Barrels )
|
||||
var arms = self.TraitsImplementing<Armament>();
|
||||
foreach (var a in arms)
|
||||
foreach(var b in a.Barrels)
|
||||
{
|
||||
var barrel = b;
|
||||
var turret = w.Turret;
|
||||
var turreted = self.TraitsImplementing<Turreted>()
|
||||
.FirstOrDefault(t => t.info.Turret == a.Info.Turret);
|
||||
var getFacing = turreted != null ? () => turreted.turretFacing :
|
||||
facing != null ? (Func<int>)(() => facing.Facing) : () => 0;
|
||||
|
||||
var muzzleFlash = new Animation(render.GetImage(self), getFacing);
|
||||
muzzleFlash.Play("muzzle");
|
||||
|
||||
muzzleFlashes.Add("muzzle{0}".F(muzzleFlashes.Count), new AnimationWithOffset(
|
||||
muzzleFlash,
|
||||
() => Combat.GetBarrelPosition(self, facing, turret, barrel).ToFloat2(),
|
||||
() => a.MuzzlePxPosition(self, facing, barrel).ToFloat2(),
|
||||
() => !isShowing));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,10 @@ namespace OpenRA.Mods.RA.Render
|
||||
|
||||
rotorAnim = new Animation(rs.GetImage(self));
|
||||
rotorAnim.PlayRepeating("rotor");
|
||||
var turret = new Turret(info.Offset);
|
||||
rs.anims.Add(info.Id, new AnimationWithOffset(
|
||||
rotorAnim,
|
||||
() => Combat.GetTurretPosition( self, facing, new Turret(info.Offset)).ToFloat2(),
|
||||
() => turret.PxPosition(self, facing).ToFloat2(),
|
||||
null ) { ZOffset = 1 } );
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -26,11 +27,11 @@ namespace OpenRA.Mods.RA
|
||||
public void Render(WorldRenderer wr, World w, ActorInfo ai, PPos centerLocation)
|
||||
{
|
||||
wr.DrawRangeCircleWithContrast(
|
||||
Color.FromArgb(128, Color.Yellow),
|
||||
centerLocation.ToFloat2(),
|
||||
ai.Traits.Get<AttackBaseInfo>().GetMaximumRange(),
|
||||
Color.FromArgb(96, Color.Black),
|
||||
1);
|
||||
Color.FromArgb(128, Color.Yellow), centerLocation.ToFloat2(),
|
||||
ai.Traits.WithInterface<ArmamentInfo>()
|
||||
.Select(a => Rules.Weapons[a.Weapon.ToLowerInvariant()].Range).Max(),
|
||||
Color.FromArgb(96, Color.Black), 1
|
||||
);
|
||||
|
||||
foreach (var a in w.ActorsWithTrait<RenderRangeCircle>())
|
||||
if (a.Actor.Owner == a.Actor.World.LocalPlayer)
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
var facing = self.Trait<IFacing>();
|
||||
var altitude = new PVecInt(0, move.Altitude);
|
||||
position = (self.CenterLocation - (PVecInt)Combat.GetTurretPosition(self, facing, smokeTurret).ToInt2());
|
||||
position = (self.CenterLocation - (PVecInt)smokeTurret.PxPosition(self, facing).ToInt2());
|
||||
|
||||
if (self.World.RenderedShroud.IsVisible(position.ToCPos()))
|
||||
self.World.AddFrameEndTask(
|
||||
|
||||
@@ -14,23 +14,24 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class TakeCoverInfo : ITraitInfo
|
||||
public class TakeCoverInfo : TurretedInfo
|
||||
{
|
||||
public readonly int ProneTime = 100; /* ticks, =4s */
|
||||
public readonly float ProneDamage = .5f;
|
||||
public readonly decimal ProneSpeed = .5m;
|
||||
public readonly int[] BarrelOffset = null;
|
||||
public readonly int[] ProneOffset = {0,-2,0,4};
|
||||
|
||||
public object Create(ActorInitializer init) { return new TakeCover(this); }
|
||||
public override object Create(ActorInitializer init) { return new TakeCover(init, this); }
|
||||
}
|
||||
|
||||
// Infantry prone behavior
|
||||
public class TakeCover : ITick, INotifyDamage, IDamageModifier, ISpeedModifier, ISync
|
||||
public class TakeCover : Turreted, ITick, INotifyDamage, IDamageModifier, ISpeedModifier, ISync
|
||||
{
|
||||
TakeCoverInfo Info;
|
||||
[Sync] int remainingProneTime = 0;
|
||||
|
||||
public TakeCover(TakeCoverInfo info)
|
||||
public TakeCover(ActorInitializer init, TakeCoverInfo info)
|
||||
: base(init, info)
|
||||
{
|
||||
Info = info;
|
||||
}
|
||||
@@ -42,36 +43,16 @@ namespace OpenRA.Mods.RA
|
||||
if (e.Damage > 0 && (e.Warhead == null || !e.Warhead.PreventProne)) /* Don't go prone when healed */
|
||||
{
|
||||
if (!IsProne)
|
||||
ApplyBarrelOffset(self, true);
|
||||
turret = new Turret(Info.ProneOffset);
|
||||
remainingProneTime = Info.ProneTime;
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
public override void Tick(Actor self)
|
||||
{
|
||||
if (IsProne)
|
||||
{
|
||||
if (--remainingProneTime == 0)
|
||||
ApplyBarrelOffset(self, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyBarrelOffset(Actor self, bool isProne)
|
||||
{
|
||||
if (Info.BarrelOffset == null)
|
||||
return;
|
||||
|
||||
var ab = self.TraitOrDefault<AttackBase>();
|
||||
if (ab == null)
|
||||
return;
|
||||
|
||||
var sign = isProne ? 1 : -1;
|
||||
foreach (var w in ab.Weapons)
|
||||
foreach (var b in w.Barrels)
|
||||
{
|
||||
b.TurretSpaceOffset += sign * new PVecInt(Info.BarrelOffset[0], Info.BarrelOffset[1]);
|
||||
b.ScreenSpaceOffset += sign * new PVecInt(Info.BarrelOffset[2], Info.BarrelOffset[3]);
|
||||
}
|
||||
base.Tick(self);
|
||||
if (IsProne && --remainingProneTime == 0)
|
||||
turret = new Turret(Info.Offset);
|
||||
}
|
||||
|
||||
public float GetDamageModifier(Actor attacker, WarheadInfo warhead )
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
alt = 0;
|
||||
facing = Turreted.GetInitialTurretFacing( init, 0 );
|
||||
pos = Combat.GetTurretPosition(self, ifacing, new Turret(info.Offset)).ToFloat2();
|
||||
pos = new Turret(info.Offset).PxPosition(self, ifacing).ToFloat2();
|
||||
|
||||
v = Game.CosmeticRandom.Gauss2D(1) * info.Spread.RelOffset();
|
||||
dfacing = Game.CosmeticRandom.Gauss1D(2) * info.ROT;
|
||||
|
||||
@@ -8,23 +8,29 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Mods.RA.Render;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class TurretedInfo : ITraitInfo, UsesInit<TurretFacingInit>
|
||||
{
|
||||
public readonly string Turret = "primary";
|
||||
public readonly int ROT = 255;
|
||||
public readonly int InitialFacing = 128;
|
||||
public readonly int[] Offset = {0,0};
|
||||
public readonly bool AlignWhenIdle = false;
|
||||
|
||||
public object Create(ActorInitializer init) { return new Turreted(init, this); }
|
||||
public virtual object Create(ActorInitializer init) { return new Turreted(init, this); }
|
||||
}
|
||||
|
||||
public class Turreted : ITick, ISync
|
||||
public class Turreted : ITick, ISync, IResolveOrder
|
||||
{
|
||||
[Sync] public int turretFacing = 0;
|
||||
public int? desiredFacing;
|
||||
TurretedInfo info;
|
||||
public TurretedInfo info;
|
||||
protected Turret turret;
|
||||
IFacing facing;
|
||||
|
||||
public static int GetInitialTurretFacing(ActorInitializer init, int def)
|
||||
@@ -43,9 +49,10 @@ namespace OpenRA.Mods.RA
|
||||
this.info = info;
|
||||
turretFacing = GetInitialTurretFacing(init, info.InitialFacing);
|
||||
facing = init.self.TraitOrDefault<IFacing>();
|
||||
turret = new Turret(info.Offset);
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
public virtual void Tick(Actor self)
|
||||
{
|
||||
var df = desiredFacing ?? ( facing != null ? facing.Facing : turretFacing );
|
||||
turretFacing = Util.TickFacing(turretFacing, df, info.ROT);
|
||||
@@ -56,5 +63,42 @@ namespace OpenRA.Mods.RA
|
||||
desiredFacing = Util.GetFacing( target.CenterLocation - self.CenterLocation, turretFacing );
|
||||
return turretFacing == desiredFacing;
|
||||
}
|
||||
|
||||
public virtual void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (info.AlignWhenIdle && order.OrderString != "Attack" && order.OrderString != "AttackHold")
|
||||
desiredFacing = null;
|
||||
}
|
||||
|
||||
public PVecFloat PxPosition(Actor self, IFacing facing)
|
||||
{
|
||||
return turret.PxPosition(self, facing);
|
||||
}
|
||||
}
|
||||
|
||||
public class Turret
|
||||
{
|
||||
public PVecInt UnitSpacePosition; // where, in the unit's local space.
|
||||
public PVecInt ScreenSpacePosition; // screen-space hack to make things line up good.
|
||||
|
||||
public Turret(int[] offset)
|
||||
{
|
||||
ScreenSpacePosition = (PVecInt) offset.AbsOffset().ToInt2();
|
||||
UnitSpacePosition = (PVecInt) offset.RelOffset().ToInt2();
|
||||
}
|
||||
|
||||
public PVecFloat PxPosition(Actor self, IFacing facing)
|
||||
{
|
||||
// Things that don't have a rotating base don't need the turrets repositioned
|
||||
if (facing == null) return ScreenSpacePosition;
|
||||
|
||||
var ru = self.TraitOrDefault<RenderUnit>();
|
||||
var numDirs = (ru != null) ? ru.anim.CurrentSequence.Facings : 8;
|
||||
var bodyFacing = facing.Facing;
|
||||
var quantizedFacing = Util.QuantizeFacing(bodyFacing, numDirs) * (256 / numDirs);
|
||||
|
||||
return (PVecFloat)Util.RotateVectorByFacing(UnitSpacePosition.ToFloat2(), quantizedFacing, .7f)
|
||||
+ (PVecFloat)ScreenSpacePosition.ToFloat2();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class Barrel
|
||||
{
|
||||
public PVecInt TurretSpaceOffset; // position in turret space
|
||||
public PVecInt ScreenSpaceOffset; // screen-space hack to make things line up good.
|
||||
public int Facing; // deviation from turret facing
|
||||
}
|
||||
|
||||
public class Turret
|
||||
{
|
||||
public float Recoil = 0.0f; // remaining recoil
|
||||
public float RecoilRecovery = 0.2f; // recoil recovery rate
|
||||
public PVecInt UnitSpacePosition; // where, in the unit's local space.
|
||||
public PVecInt ScreenSpacePosition; // screen-space hack to make things line up good.
|
||||
|
||||
public Turret(int[] offset, float recoilRecovery)
|
||||
{
|
||||
ScreenSpacePosition = (PVecInt) offset.AbsOffset().ToInt2();
|
||||
UnitSpacePosition = (PVecInt) offset.RelOffset().ToInt2();
|
||||
RecoilRecovery = recoilRecovery;
|
||||
}
|
||||
|
||||
public Turret(int[] offset) : this(offset, 0) {}
|
||||
}
|
||||
|
||||
public class Weapon
|
||||
{
|
||||
public WeaponInfo Info;
|
||||
public int FireDelay = 0; // time (in frames) until the weapon can fire again
|
||||
public int Burst = 0; // burst counter
|
||||
public int Recoil = 0;
|
||||
|
||||
public Barrel[] Barrels; // where projectiles are spawned, in local turret space.
|
||||
public Turret Turret; // where this weapon is mounted -- possibly shared
|
||||
|
||||
public Weapon(string weaponName, Turret turret, int[] localOffset, int recoil)
|
||||
{
|
||||
Info = Rules.Weapons[weaponName.ToLowerInvariant()];
|
||||
Burst = Info.Burst;
|
||||
Turret = turret;
|
||||
Recoil = recoil;
|
||||
|
||||
var barrels = new List<Barrel>();
|
||||
for (var i = 0; i < localOffset.Length / 5; i++)
|
||||
barrels.Add(new Barrel
|
||||
{
|
||||
TurretSpaceOffset = new PVecInt(localOffset[5 * i], localOffset[5 * i + 1]),
|
||||
ScreenSpaceOffset = new PVecInt(localOffset[5 * i + 2], localOffset[5 * i + 3]),
|
||||
Facing = localOffset[5 * i + 4]
|
||||
});
|
||||
|
||||
// if no barrels specified, the default is "turret position; turret facing".
|
||||
if (barrels.Count == 0)
|
||||
barrels.Add(new Barrel { TurretSpaceOffset = PVecInt.Zero, ScreenSpaceOffset = PVecInt.Zero, Facing = 0 });
|
||||
|
||||
Barrels = barrels.ToArray();
|
||||
}
|
||||
|
||||
public bool IsReloading { get { return FireDelay > 0; } }
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (FireDelay > 0) --FireDelay;
|
||||
Turret.Recoil = Math.Max(0f, Turret.Recoil - Turret.RecoilRecovery);
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(World world, Target target)
|
||||
{
|
||||
if( target.IsActor )
|
||||
return Combat.WeaponValidForTarget( Info, target.Actor );
|
||||
else
|
||||
return Combat.WeaponValidForTarget( Info, world, target.CenterLocation.ToCPos() );
|
||||
}
|
||||
|
||||
public void FiredShot()
|
||||
{
|
||||
Turret.Recoil = this.Recoil;
|
||||
|
||||
if (--Burst > 0)
|
||||
FireDelay = Info.BurstDelay;
|
||||
else
|
||||
{
|
||||
FireDelay = Info.ROF;
|
||||
Burst = Info.Burst;
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckFire(Actor self, AttackBase attack, IMove move, IFacing facing, Target target)
|
||||
{
|
||||
if (FireDelay > 0) return;
|
||||
|
||||
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();
|
||||
if (limitedAmmo != null && !limitedAmmo.HasAmmo())
|
||||
return;
|
||||
|
||||
if (!Combat.IsInRange(self.CenterLocation, Info.Range, target)) return;
|
||||
if (Combat.IsInRange(self.CenterLocation, Info.MinRange, target)) return;
|
||||
|
||||
if (!IsValidAgainst(self.World, target)) return;
|
||||
|
||||
var barrel = Barrels[Burst % Barrels.Length];
|
||||
var destMove = target.IsActor ? target.Actor.TraitOrDefault<IMove>() : null;
|
||||
var turreted = self.TraitOrDefault<Turreted>();
|
||||
|
||||
var args = new ProjectileArgs
|
||||
{
|
||||
weapon = Info,
|
||||
|
||||
firedBy = self,
|
||||
target = target,
|
||||
|
||||
src = (self.CenterLocation + (PVecInt)Combat.GetBarrelPosition(self, facing, Turret, barrel).ToInt2()),
|
||||
srcAltitude = move != null ? move.Altitude : 0,
|
||||
dest = target.CenterLocation,
|
||||
destAltitude = destMove != null ? destMove.Altitude : 0,
|
||||
|
||||
facing = barrel.Facing +
|
||||
(turreted != null ? turreted.turretFacing :
|
||||
facing != null ? facing.Facing : Util.GetFacing(target.CenterLocation - self.CenterLocation, 0)),
|
||||
|
||||
firepowerModifier = self.TraitsImplementing<IFirepowerModifier>()
|
||||
.Select(a => a.GetFirepowerModifier())
|
||||
.Product()
|
||||
};
|
||||
|
||||
attack.ScheduleDelayedAction( attack.FireDelay( self, target, self.Info.Traits.Get<AttackBaseInfo>() ), () =>
|
||||
{
|
||||
if (args.weapon.Projectile != null)
|
||||
{
|
||||
var projectile = args.weapon.Projectile.Create(args);
|
||||
if (projectile != null)
|
||||
self.World.Add(projectile);
|
||||
|
||||
if (args.weapon.Report != null && args.weapon.Report.Any())
|
||||
Sound.Play(args.weapon.Report.Random(self.World.SharedRandom) + ".aud", self.CenterLocation);
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var na in self.TraitsImplementing<INotifyAttack>())
|
||||
na.Attacking(self, target);
|
||||
|
||||
FiredShot();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user