Merge pull request #7324 from reaperrr/limitedammo2
Refactored LimitedAmmo to AmmoPool
This commit is contained in:
@@ -229,7 +229,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
var unit = buildableThings.Random(Random);
|
var unit = buildableThings.Random(Random);
|
||||||
return HasAdequateAirUnits(unit) ? unit : null;
|
return HasAdequateAirUnitReloadBuildings(unit) ? unit : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ActorInfo ChooseUnitToBuild(ProductionQueue queue)
|
ActorInfo ChooseUnitToBuild(ProductionQueue queue)
|
||||||
@@ -246,7 +246,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
foreach (var unit in Info.UnitsToBuild.Shuffle(Random))
|
foreach (var unit in Info.UnitsToBuild.Shuffle(Random))
|
||||||
if (buildableThings.Any(b => b.Name == unit.Key))
|
if (buildableThings.Any(b => b.Name == unit.Key))
|
||||||
if (myUnits.Count(a => a == unit.Key) < unit.Value * myUnits.Count)
|
if (myUnits.Count(a => a == unit.Key) < unit.Value * myUnits.Count)
|
||||||
if (HasAdequateAirUnits(Map.Rules.Actors[unit.Key]))
|
if (HasAdequateAirUnitReloadBuildings(Map.Rules.Actors[unit.Key]))
|
||||||
return Map.Rules.Actors[unit.Key];
|
return Map.Rules.Actors[unit.Key];
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -318,13 +318,18 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For mods like RA (number of building must match the number of aircraft)
|
// For mods like RA (number of building must match the number of aircraft)
|
||||||
bool HasAdequateAirUnits(ActorInfo actorInfo)
|
bool HasAdequateAirUnitReloadBuildings(ActorInfo actorInfo)
|
||||||
{
|
{
|
||||||
if (!actorInfo.Traits.Contains<ReloadsInfo>() && actorInfo.Traits.Contains<LimitedAmmoInfo>()
|
var aircraftInfo = actorInfo.Traits.GetOrDefault<AircraftInfo>();
|
||||||
&& actorInfo.Traits.Contains<AircraftInfo>())
|
if (aircraftInfo == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var ammoPoolsInfo = actorInfo.Traits.WithInterface<AmmoPoolInfo>();
|
||||||
|
|
||||||
|
if (ammoPoolsInfo.Any(x => !x.SelfReloads))
|
||||||
{
|
{
|
||||||
var countOwnAir = CountUnits(actorInfo.Name, Player);
|
var countOwnAir = CountUnits(actorInfo.Name, Player);
|
||||||
var countBuildings = CountBuilding(actorInfo.Traits.Get<AircraftInfo>().RearmBuildings.FirstOrDefault(), Player);
|
var countBuildings = CountBuilding(aircraftInfo.RearmBuildings.FirstOrDefault(), Player);
|
||||||
if (countOwnAir >= countBuildings)
|
if (countOwnAir >= countBuildings)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,19 +104,20 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
|
|
||||||
protected static bool FullAmmo(Actor a)
|
protected static bool FullAmmo(Actor a)
|
||||||
{
|
{
|
||||||
var limitedAmmo = a.TraitOrDefault<LimitedAmmo>();
|
var ammoPools = a.TraitsImplementing<AmmoPool>();
|
||||||
return limitedAmmo != null && limitedAmmo.FullAmmo();
|
return ammoPools.All(x => x.FullAmmo());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static bool HasAmmo(Actor a)
|
protected static bool HasAmmo(Actor a)
|
||||||
{
|
{
|
||||||
var limitedAmmo = a.TraitOrDefault<LimitedAmmo>();
|
var ammoPools = a.TraitsImplementing<AmmoPool>();
|
||||||
return limitedAmmo != null && limitedAmmo.HasAmmo();
|
return ammoPools.All(x => x.HasAmmo());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static bool ReloadsAutomatically(Actor a)
|
protected static bool ReloadsAutomatically(Actor a)
|
||||||
{
|
{
|
||||||
return a.HasTrait<Reloads>();
|
var ammoPools = a.TraitsImplementing<AmmoPool>();
|
||||||
|
return ammoPools.All(x => x.Info.SelfReloads);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static bool IsRearm(Actor a)
|
protected static bool IsRearm(Actor a)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Activities;
|
using OpenRA.Activities;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
@@ -18,30 +20,38 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
public class FlyAttack : Activity
|
public class FlyAttack : Activity
|
||||||
{
|
{
|
||||||
readonly Target target;
|
readonly Target target;
|
||||||
|
readonly AttackPlane attackPlane;
|
||||||
|
readonly IEnumerable<AmmoPool> ammoPools;
|
||||||
Activity inner;
|
Activity inner;
|
||||||
int ticksUntilTurn = 50;
|
int ticksUntilTurn = 50;
|
||||||
|
|
||||||
public FlyAttack(Target target) { this.target = target; }
|
public FlyAttack(Actor self, Target target)
|
||||||
|
{
|
||||||
|
this.target = target;
|
||||||
|
attackPlane = self.TraitOrDefault<AttackPlane>();
|
||||||
|
ammoPools = self.TraitsImplementing<AmmoPool>();
|
||||||
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (!target.IsValidFor(self))
|
if (!target.IsValidFor(self))
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();
|
// Move to the next activity only if all ammo pools are depleted and none reload automatically
|
||||||
if (limitedAmmo != null && !limitedAmmo.HasAmmo())
|
// TODO: This should check whether there is ammo left that is actually suitable for the target
|
||||||
|
if (ammoPools != null && ammoPools.All(x => !x.Info.SelfReloads && !x.HasAmmo()))
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
var attack = self.TraitOrDefault<AttackPlane>();
|
if (attackPlane != null)
|
||||||
if (attack != null)
|
attackPlane.DoAttack(self, target);
|
||||||
attack.DoAttack(self, target);
|
|
||||||
|
|
||||||
if (inner == null)
|
if (inner == null)
|
||||||
{
|
{
|
||||||
if (IsCanceled)
|
if (IsCanceled)
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
if (target.IsInRange(self.CenterPosition, attack.Armaments.Select(a => a.Weapon.MinRange).Min()))
|
// TODO: This should fire each weapon at its maximum range
|
||||||
|
if (target.IsInRange(self.CenterPosition, attackPlane.Armaments.Select(a => a.Weapon.MinRange).Min()))
|
||||||
inner = Util.SequenceActivities(new FlyTimed(ticksUntilTurn), new Fly(self, target), new FlyTimed(ticksUntilTurn));
|
inner = Util.SequenceActivities(new FlyTimed(ticksUntilTurn), new Fly(self, target), new FlyTimed(ticksUntilTurn));
|
||||||
else
|
else
|
||||||
inner = Util.SequenceActivities(new Fly(self, target), new FlyTimed(ticksUntilTurn));
|
inner = Util.SequenceActivities(new Fly(self, target), new FlyTimed(ticksUntilTurn));
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using OpenRA.Activities;
|
using OpenRA.Activities;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
@@ -16,21 +19,29 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
{
|
{
|
||||||
public class HeliAttack : Activity
|
public class HeliAttack : Activity
|
||||||
{
|
{
|
||||||
Target target;
|
readonly Target target;
|
||||||
public HeliAttack(Target target) { this.target = target; }
|
readonly Helicopter helicopter;
|
||||||
|
readonly AttackHeli attackHeli;
|
||||||
|
readonly IEnumerable<AmmoPool> ammoPools;
|
||||||
|
|
||||||
|
public HeliAttack(Actor self, Target target)
|
||||||
|
{
|
||||||
|
this.target = target;
|
||||||
|
helicopter = self.Trait<Helicopter>();
|
||||||
|
attackHeli = self.Trait<AttackHeli>();
|
||||||
|
ammoPools = self.TraitsImplementing<AmmoPool>();
|
||||||
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled || !target.IsValidFor(self))
|
if (IsCanceled || !target.IsValidFor(self))
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();
|
// If all ammo pools are depleted and none reload automatically, return to helipad to reload and then move to next activity
|
||||||
var reloads = self.TraitOrDefault<Reloads>();
|
// TODO: This should check whether there is ammo left that is actually suitable for the target
|
||||||
if (limitedAmmo != null && !limitedAmmo.HasAmmo() && reloads == null)
|
if (ammoPools != null && ammoPools.All(x => !x.Info.SelfReloads && !x.HasAmmo()))
|
||||||
return Util.SequenceActivities(new HeliReturn(), NextActivity);
|
return Util.SequenceActivities(new HeliReturn(), NextActivity);
|
||||||
|
|
||||||
var helicopter = self.Trait<Helicopter>();
|
|
||||||
var attack = self.Trait<AttackHeli>();
|
|
||||||
var dist = target.CenterPosition - self.CenterPosition;
|
var dist = target.CenterPosition - self.CenterPosition;
|
||||||
|
|
||||||
// Can rotate facing while ascending
|
// Can rotate facing while ascending
|
||||||
@@ -41,10 +52,12 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
return this;
|
return this;
|
||||||
|
|
||||||
// Fly towards the target
|
// Fly towards the target
|
||||||
if (!target.IsInRange(self.CenterPosition, attack.GetMaximumRange()))
|
// TODO: Fix that the helicopter won't do anything if it has multiple weapons with different ranges
|
||||||
|
// and the weapon with the longest range is out of ammo
|
||||||
|
if (!target.IsInRange(self.CenterPosition, attackHeli.GetMaximumRange()))
|
||||||
helicopter.SetPosition(self, helicopter.CenterPosition + helicopter.FlyStep(desiredFacing));
|
helicopter.SetPosition(self, helicopter.CenterPosition + helicopter.FlyStep(desiredFacing));
|
||||||
|
|
||||||
attack.DoAttack(self, target);
|
attackHeli.DoAttack(self, target);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,53 +8,64 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Activities;
|
using OpenRA.Activities;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
using OpenRA.Traits;
|
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Activities
|
namespace OpenRA.Mods.Common.Activities
|
||||||
{
|
{
|
||||||
public class Rearm : Activity
|
public class Rearm : Activity
|
||||||
{
|
{
|
||||||
readonly LimitedAmmo limitedAmmo;
|
readonly IEnumerable<AmmoPool> ammoPools;
|
||||||
int ticksPerPip = 25 * 2;
|
readonly Dictionary<AmmoPool, int> ammoPoolsReloadTimes;
|
||||||
int remainingTicks = 25 * 2;
|
|
||||||
string sound = null;
|
|
||||||
|
|
||||||
public Rearm(Actor self, string sound = null)
|
public Rearm(Actor self)
|
||||||
{
|
{
|
||||||
limitedAmmo = self.TraitOrDefault<LimitedAmmo>();
|
ammoPools = self.TraitsImplementing<AmmoPool>().Where(p => !p.Info.SelfReloads);
|
||||||
if (limitedAmmo != null)
|
|
||||||
ticksPerPip = limitedAmmo.ReloadTimePerAmmo();
|
if (ammoPools == null)
|
||||||
remainingTicks = ticksPerPip;
|
return;
|
||||||
this.sound = sound;
|
|
||||||
|
ammoPoolsReloadTimes = ammoPools.ToDictionary(x => x, y => y.Info.ReloadTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled || limitedAmmo == null)
|
if (IsCanceled || ammoPoolsReloadTimes == null)
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
if (--remainingTicks == 0)
|
var needsReloading = false;
|
||||||
|
|
||||||
|
foreach (var pool in ammoPools)
|
||||||
{
|
{
|
||||||
var hostBuilding = self.World.ActorMap.GetUnitsAt(self.Location)
|
if (pool.FullAmmo())
|
||||||
.FirstOrDefault(a => a.HasTrait<RenderBuilding>());
|
continue;
|
||||||
|
|
||||||
|
needsReloading = true;
|
||||||
|
|
||||||
|
if (--ammoPoolsReloadTimes[pool] > 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// HACK to check if we are on the helipad/airfield/etc.
|
||||||
|
var hostBuilding = self.World.ActorMap.GetUnitsAt(self.Location).FirstOrDefault(a => a.HasTrait<RenderBuilding>());
|
||||||
|
|
||||||
if (hostBuilding == null || !hostBuilding.IsInWorld)
|
if (hostBuilding == null || !hostBuilding.IsInWorld)
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
if (!limitedAmmo.GiveAmmo())
|
if (!pool.GiveAmmo())
|
||||||
return NextActivity;
|
continue;
|
||||||
|
|
||||||
hostBuilding.Trait<RenderBuilding>().PlayCustomAnim(hostBuilding, "active");
|
hostBuilding.Trait<RenderBuilding>().PlayCustomAnim(hostBuilding, "active");
|
||||||
|
|
||||||
|
var sound = pool.Info.RearmSound;
|
||||||
if (sound != null)
|
if (sound != null)
|
||||||
Sound.Play(sound, self.CenterPosition);
|
Sound.Play(sound, self.CenterPosition);
|
||||||
|
|
||||||
remainingTicks = limitedAmmo.ReloadTimePerAmmo();
|
ammoPoolsReloadTimes[pool] = pool.Info.ReloadTicks;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return needsReloading ? this : NextActivity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,7 +320,7 @@
|
|||||||
<Compile Include="Traits\Invulnerable.cs" />
|
<Compile Include="Traits\Invulnerable.cs" />
|
||||||
<Compile Include="Traits\JamsMissiles.cs" />
|
<Compile Include="Traits\JamsMissiles.cs" />
|
||||||
<Compile Include="Traits\KillsSelf.cs" />
|
<Compile Include="Traits\KillsSelf.cs" />
|
||||||
<Compile Include="Traits\LimitedAmmo.cs" />
|
<Compile Include="Traits\AmmoPool.cs" />
|
||||||
<Compile Include="Traits\Mobile.cs" />
|
<Compile Include="Traits\Mobile.cs" />
|
||||||
<Compile Include="Traits\Modifiers\DisabledOverlay.cs" />
|
<Compile Include="Traits\Modifiers\DisabledOverlay.cs" />
|
||||||
<Compile Include="Traits\Modifiers\FrozenUnderFog.cs" />
|
<Compile Include="Traits\Modifiers\FrozenUnderFog.cs" />
|
||||||
@@ -364,7 +364,6 @@
|
|||||||
<Compile Include="Traits\ProximityCaptor.cs" />
|
<Compile Include="Traits\ProximityCaptor.cs" />
|
||||||
<Compile Include="Traits\ProximityCapturable.cs" />
|
<Compile Include="Traits\ProximityCapturable.cs" />
|
||||||
<Compile Include="Traits\RadarColorFromTerrain.cs" />
|
<Compile Include="Traits\RadarColorFromTerrain.cs" />
|
||||||
<Compile Include="Traits\Reloads.cs" />
|
|
||||||
<Compile Include="Traits\Render\Hovers.cs" />
|
<Compile Include="Traits\Render\Hovers.cs" />
|
||||||
<Compile Include="Traits\Render\RenderBuilding.cs" />
|
<Compile Include="Traits\Render\RenderBuilding.cs" />
|
||||||
<Compile Include="Traits\Render\RenderBuildingCharge.cs" />
|
<Compile Include="Traits\Render\RenderBuildingCharge.cs" />
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ namespace OpenRA.Mods.Common.Scripting
|
|||||||
[Desc("Fly an attack against the target actor.")]
|
[Desc("Fly an attack against the target actor.")]
|
||||||
public void Attack(Actor target)
|
public void Attack(Actor target)
|
||||||
{
|
{
|
||||||
Self.QueueActivity(new FlyAttack(Target.FromActor(target)));
|
Self.QueueActivity(new FlyAttack(Self, Target.FromActor(target)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||||
{
|
{
|
||||||
return new HeliAttack(newTarget);
|
return new HeliAttack(self, newTarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||||
{
|
{
|
||||||
return new FlyAttack(newTarget);
|
return new FlyAttack(self, newTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CanAttack(Actor self, Target target)
|
protected override bool CanAttack(Actor self, Target target)
|
||||||
|
|||||||
138
OpenRA.Mods.Common/Traits/AmmoPool.cs
Normal file
138
OpenRA.Mods.Common/Traits/AmmoPool.cs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2015 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.Common.Traits
|
||||||
|
{
|
||||||
|
[Desc("Actor has a limited amount of ammo, after using it all the actor must reload in some way.")]
|
||||||
|
public class AmmoPoolInfo : ITraitInfo
|
||||||
|
{
|
||||||
|
[Desc("Name of this ammo pool, used to link armaments to this pool.")]
|
||||||
|
public readonly string Name = "primary";
|
||||||
|
|
||||||
|
[Desc("How much ammo does this pool contain when fully loaded.")]
|
||||||
|
public readonly int Ammo = 1;
|
||||||
|
|
||||||
|
[Desc("Initial ammo the actor is created with. Defaults to Ammo.")]
|
||||||
|
public readonly int InitialAmmo = -1;
|
||||||
|
|
||||||
|
[Desc("Defaults to value in Ammo. 0 means no visible pips.")]
|
||||||
|
public readonly int PipCount = -1;
|
||||||
|
|
||||||
|
[Desc("PipType to use for loaded ammo.")]
|
||||||
|
public readonly PipType PipType = PipType.Green;
|
||||||
|
|
||||||
|
[Desc("PipType to use for empty ammo.")]
|
||||||
|
public readonly PipType PipTypeEmpty = PipType.Transparent;
|
||||||
|
|
||||||
|
[Desc("How much ammo is reloaded after a certain period.")]
|
||||||
|
public readonly int ReloadCount = 1;
|
||||||
|
|
||||||
|
[Desc("Sound to play for each reloaded ammo magazine.")]
|
||||||
|
public readonly string RearmSound = null;
|
||||||
|
|
||||||
|
[Desc("Time to reload per ReloadCount on airfield etc.")]
|
||||||
|
public readonly int ReloadTicks = 25 * 2;
|
||||||
|
|
||||||
|
[Desc("Whether or not ammo is replenished on its own.")]
|
||||||
|
public readonly bool SelfReloads = false;
|
||||||
|
|
||||||
|
[Desc("Time to reload per ReloadCount when actor 'SelfReloads'.")]
|
||||||
|
public readonly int SelfReloadTicks = 25 * 2;
|
||||||
|
|
||||||
|
[Desc("Whether or not reload timer should be reset when ammo has been fired.")]
|
||||||
|
public readonly bool ResetOnFire = false;
|
||||||
|
|
||||||
|
public object Create(ActorInitializer init) { return new AmmoPool(init.Self, this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AmmoPool : INotifyAttack, IPips, ITick, ISync
|
||||||
|
{
|
||||||
|
public readonly AmmoPoolInfo Info;
|
||||||
|
[Sync] public int CurrentAmmo;
|
||||||
|
[Sync] public int RemainingTicks;
|
||||||
|
public int PreviousAmmo;
|
||||||
|
|
||||||
|
public AmmoPool(Actor self, AmmoPoolInfo info)
|
||||||
|
{
|
||||||
|
Info = info;
|
||||||
|
if (Info.InitialAmmo < Info.Ammo && Info.InitialAmmo >= 0)
|
||||||
|
CurrentAmmo = Info.InitialAmmo;
|
||||||
|
else
|
||||||
|
CurrentAmmo = Info.Ammo;
|
||||||
|
|
||||||
|
RemainingTicks = Info.SelfReloadTicks;
|
||||||
|
PreviousAmmo = GetAmmoCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetAmmoCount() { return CurrentAmmo; }
|
||||||
|
public bool FullAmmo() { return CurrentAmmo == Info.Ammo; }
|
||||||
|
public bool HasAmmo() { return CurrentAmmo > 0; }
|
||||||
|
|
||||||
|
public bool GiveAmmo()
|
||||||
|
{
|
||||||
|
if (CurrentAmmo >= Info.Ammo)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
++CurrentAmmo;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TakeAmmo()
|
||||||
|
{
|
||||||
|
if (CurrentAmmo <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
--CurrentAmmo;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Attacking(Actor self, Target target, Armament a, Barrel barrel)
|
||||||
|
{
|
||||||
|
if (a.Info.AmmoPoolName == Info.Name)
|
||||||
|
TakeAmmo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Tick(Actor self)
|
||||||
|
{
|
||||||
|
if (!Info.SelfReloads)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Resets the tick counter if ammo was fired.
|
||||||
|
if (Info.ResetOnFire && GetAmmoCount() < PreviousAmmo)
|
||||||
|
{
|
||||||
|
RemainingTicks = Info.SelfReloadTicks;
|
||||||
|
PreviousAmmo = GetAmmoCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FullAmmo() && --RemainingTicks == 0)
|
||||||
|
{
|
||||||
|
RemainingTicks = Info.SelfReloadTicks;
|
||||||
|
|
||||||
|
for (var i = 0; i < Info.ReloadCount; i++)
|
||||||
|
GiveAmmo();
|
||||||
|
|
||||||
|
PreviousAmmo = GetAmmoCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<PipType> GetPips(Actor self)
|
||||||
|
{
|
||||||
|
var pips = Info.PipCount >= 0 ? Info.PipCount : Info.Ammo;
|
||||||
|
|
||||||
|
return Enumerable.Range(0, pips).Select(i =>
|
||||||
|
(CurrentAmmo * pips) / Info.Ammo > i ?
|
||||||
|
Info.PipType : Info.PipTypeEmpty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,16 +31,25 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
[WeaponReference]
|
[WeaponReference]
|
||||||
[Desc("Has to be defined here and in weapons.yaml.")]
|
[Desc("Has to be defined here and in weapons.yaml.")]
|
||||||
public readonly string Weapon = null;
|
public readonly string Weapon = null;
|
||||||
|
|
||||||
|
[Desc("Which limited ammo pool (if present) should this armament be assigned to.")]
|
||||||
|
public readonly string AmmoPoolName = "primary";
|
||||||
|
|
||||||
|
[Desc("Which turret (if present) should this armament be assigned to.")]
|
||||||
public readonly string Turret = "primary";
|
public readonly string Turret = "primary";
|
||||||
|
|
||||||
[Desc("Time (in frames) until the weapon can fire again.")]
|
[Desc("Time (in frames) until the weapon can fire again.")]
|
||||||
public readonly int FireDelay = 0;
|
public readonly int FireDelay = 0;
|
||||||
|
|
||||||
[Desc("Muzzle position relative to turret or body. (forward, right, up) triples")]
|
[Desc("Muzzle position relative to turret or body. (forward, right, up) triples")]
|
||||||
public readonly WVec[] LocalOffset = { };
|
public readonly WVec[] LocalOffset = { };
|
||||||
|
|
||||||
[Desc("Muzzle yaw relative to turret or body.")]
|
[Desc("Muzzle yaw relative to turret or body.")]
|
||||||
public readonly WAngle[] LocalYaw = { };
|
public readonly WAngle[] LocalYaw = { };
|
||||||
|
|
||||||
[Desc("Move the turret backwards when firing.")]
|
[Desc("Move the turret backwards when firing.")]
|
||||||
public readonly WRange Recoil = WRange.Zero;
|
public readonly WRange Recoil = WRange.Zero;
|
||||||
|
|
||||||
[Desc("Recoil recovery per-frame")]
|
[Desc("Recoil recovery per-frame")]
|
||||||
public readonly WRange RecoilRecovery = new WRange(9);
|
public readonly WRange RecoilRecovery = new WRange(9);
|
||||||
|
|
||||||
@@ -64,7 +73,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
readonly Actor self;
|
readonly Actor self;
|
||||||
Lazy<Turreted> turret;
|
Lazy<Turreted> turret;
|
||||||
Lazy<IBodyOrientation> coords;
|
Lazy<IBodyOrientation> coords;
|
||||||
Lazy<LimitedAmmo> limitedAmmo;
|
Lazy<AmmoPool> ammoPool;
|
||||||
List<Pair<int, Action>> delayedActions = new List<Pair<int, Action>>();
|
List<Pair<int, Action>> delayedActions = new List<Pair<int, Action>>();
|
||||||
|
|
||||||
public WRange Recoil;
|
public WRange Recoil;
|
||||||
@@ -79,7 +88,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
// We can't resolve these until runtime
|
// We can't resolve these until runtime
|
||||||
turret = Exts.Lazy(() => self.TraitsImplementing<Turreted>().FirstOrDefault(t => t.Name == info.Turret));
|
turret = Exts.Lazy(() => self.TraitsImplementing<Turreted>().FirstOrDefault(t => t.Name == info.Turret));
|
||||||
coords = Exts.Lazy(() => self.Trait<IBodyOrientation>());
|
coords = Exts.Lazy(() => self.Trait<IBodyOrientation>());
|
||||||
limitedAmmo = Exts.Lazy(() => self.TraitOrDefault<LimitedAmmo>());
|
ammoPool = Exts.Lazy(() => self.TraitsImplementing<AmmoPool>().FirstOrDefault(la => la.Info.Name == info.AmmoPoolName));
|
||||||
|
|
||||||
Weapon = self.World.Map.Rules.Weapons[info.Weapon.ToLowerInvariant()];
|
Weapon = self.World.Map.Rules.Weapons[info.Weapon.ToLowerInvariant()];
|
||||||
Burst = Weapon.Burst;
|
Burst = Weapon.Burst;
|
||||||
@@ -136,7 +145,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (IsReloading)
|
if (IsReloading)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (limitedAmmo.Value != null && !limitedAmmo.Value.HasAmmo())
|
if (ammoPool.Value != null && !ammoPool.Value.HasAmmo())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (!target.IsInRange(self.CenterPosition, Weapon.Range))
|
if (!target.IsInRange(self.CenterPosition, Weapon.Range))
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2015 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.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using OpenRA.Traits;
|
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Traits
|
|
||||||
{
|
|
||||||
[Desc("Actor has a limited amount of ammo, after using it all the actor must reload in some way.")]
|
|
||||||
public class LimitedAmmoInfo : ITraitInfo
|
|
||||||
{
|
|
||||||
public readonly int Ammo = 0;
|
|
||||||
|
|
||||||
[Desc("Defaults to value in Ammo.")]
|
|
||||||
public readonly int PipCount = 0;
|
|
||||||
|
|
||||||
public readonly PipType PipType = PipType.Green;
|
|
||||||
public readonly PipType PipTypeEmpty = PipType.Transparent;
|
|
||||||
|
|
||||||
[Desc("Time to reload measured in ticks.")]
|
|
||||||
public readonly int ReloadTicks = 25 * 2;
|
|
||||||
|
|
||||||
public object Create(ActorInitializer init) { return new LimitedAmmo(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LimitedAmmo : INotifyAttack, IPips, ISync
|
|
||||||
{
|
|
||||||
[Sync] int ammo;
|
|
||||||
LimitedAmmoInfo info;
|
|
||||||
|
|
||||||
public LimitedAmmo(LimitedAmmoInfo info)
|
|
||||||
{
|
|
||||||
ammo = info.Ammo;
|
|
||||||
this.info = info;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool FullAmmo() { return ammo == info.Ammo; }
|
|
||||||
public bool HasAmmo() { return ammo > 0; }
|
|
||||||
public bool GiveAmmo()
|
|
||||||
{
|
|
||||||
if (ammo >= info.Ammo) return false;
|
|
||||||
++ammo;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TakeAmmo()
|
|
||||||
{
|
|
||||||
if (ammo <= 0) return false;
|
|
||||||
--ammo;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ReloadTimePerAmmo() { return info.ReloadTicks; }
|
|
||||||
|
|
||||||
public void Attacking(Actor self, Target target, Armament a, Barrel barrel) { TakeAmmo(); }
|
|
||||||
|
|
||||||
public int GetAmmoCount() { return ammo; }
|
|
||||||
|
|
||||||
public IEnumerable<PipType> GetPips(Actor self)
|
|
||||||
{
|
|
||||||
var pips = info.PipCount != 0 ? info.PipCount : info.Ammo;
|
|
||||||
return Enumerable.Range(0, pips).Select(i =>
|
|
||||||
(ammo * pips) / info.Ammo > i ?
|
|
||||||
info.PipType : info.PipTypeEmpty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2015 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 OpenRA.Traits;
|
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Traits
|
|
||||||
{
|
|
||||||
[Desc("Unit will reload its limited ammo itself.")]
|
|
||||||
public class ReloadsInfo : ITraitInfo, Requires<LimitedAmmoInfo>
|
|
||||||
{
|
|
||||||
[Desc("How much ammo is reloaded after a certain period.")]
|
|
||||||
public readonly int Count = 0;
|
|
||||||
[Desc("How long it takes to do so.")]
|
|
||||||
public readonly int Period = 50;
|
|
||||||
[Desc("Whether or not reload counter should be reset when ammo has been fired.")]
|
|
||||||
public readonly bool ResetOnFire = false;
|
|
||||||
|
|
||||||
public object Create(ActorInitializer init) { return new Reloads(init.Self, this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Reloads : ITick
|
|
||||||
{
|
|
||||||
[Sync] int remainingTicks;
|
|
||||||
ReloadsInfo info;
|
|
||||||
LimitedAmmo la;
|
|
||||||
int previousAmmo;
|
|
||||||
|
|
||||||
public Reloads(Actor self, ReloadsInfo info)
|
|
||||||
{
|
|
||||||
this.info = info;
|
|
||||||
remainingTicks = info.Period;
|
|
||||||
la = self.Trait<LimitedAmmo>();
|
|
||||||
previousAmmo = la.GetAmmoCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Tick(Actor self)
|
|
||||||
{
|
|
||||||
if (!la.FullAmmo() && --remainingTicks == 0)
|
|
||||||
{
|
|
||||||
remainingTicks = info.Period;
|
|
||||||
|
|
||||||
for (var i = 0; i < info.Count; i++)
|
|
||||||
la.GiveAmmo();
|
|
||||||
|
|
||||||
previousAmmo = la.GetAmmoCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resets the tick counter if ammo was fired.
|
|
||||||
if (info.ResetOnFire && la.GetAmmoCount() < previousAmmo)
|
|
||||||
{
|
|
||||||
remainingTicks = info.Period;
|
|
||||||
previousAmmo = la.GetAmmoCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,18 +22,20 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
class RepairableInfo : ITraitInfo, Requires<HealthInfo>
|
class RepairableInfo : ITraitInfo, Requires<HealthInfo>
|
||||||
{
|
{
|
||||||
public readonly string[] RepairBuildings = { "fix" };
|
public readonly string[] RepairBuildings = { "fix" };
|
||||||
public virtual object Create(ActorInitializer init) { return new Repairable(init.Self); }
|
public virtual object Create(ActorInitializer init) { return new Repairable(init.Self, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
class Repairable : IIssueOrder, IResolveOrder, IOrderVoice
|
class Repairable : IIssueOrder, IResolveOrder, IOrderVoice
|
||||||
{
|
{
|
||||||
readonly Actor self;
|
readonly RepairableInfo info;
|
||||||
readonly Health health;
|
readonly Health health;
|
||||||
|
readonly IEnumerable<AmmoPool> ammoPools;
|
||||||
|
|
||||||
public Repairable(Actor self)
|
public Repairable(Actor self, RepairableInfo info)
|
||||||
{
|
{
|
||||||
this.self = self;
|
this.info = info;
|
||||||
health = self.Trait<Health>();
|
health = self.Trait<Health>();
|
||||||
|
ammoPools = self.TraitsImplementing<AmmoPool>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IOrderTargeter> Orders
|
public IEnumerable<IOrderTargeter> Orders
|
||||||
@@ -41,7 +43,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
yield return new EnterAlliedActorTargeter<Building>("Repair", 5,
|
yield return new EnterAlliedActorTargeter<Building>("Repair", 5,
|
||||||
target => CanRepairAt(target), _ => CanRepair());
|
target => CanRepairAt(target), _ => CanRepair() || CanRearm());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,13 +57,25 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
bool CanRepairAt(Actor target)
|
bool CanRepairAt(Actor target)
|
||||||
{
|
{
|
||||||
return self.Info.Traits.Get<RepairableInfo>().RepairBuildings.Contains(target.Info.Name);
|
return info.RepairBuildings.Contains(target.Info.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanRearmAt(Actor target)
|
||||||
|
{
|
||||||
|
return info.RepairBuildings.Contains(target.Info.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanRepair()
|
bool CanRepair()
|
||||||
{
|
{
|
||||||
var li = self.TraitOrDefault<LimitedAmmo>();
|
return health.DamageState > DamageState.Undamaged;
|
||||||
return health.DamageState > DamageState.Undamaged || (li != null && !li.FullAmmo());
|
}
|
||||||
|
|
||||||
|
bool CanRearm()
|
||||||
|
{
|
||||||
|
if (ammoPools != null)
|
||||||
|
return ammoPools.Any(x => !x.Info.SelfReloads && !x.FullAmmo());
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string VoicePhraseForOrder(Actor self, Order order)
|
public string VoicePhraseForOrder(Actor self, Order order)
|
||||||
@@ -73,7 +87,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
{
|
{
|
||||||
if (order.OrderString == "Repair")
|
if (order.OrderString == "Repair")
|
||||||
{
|
{
|
||||||
if (!CanRepairAt(order.TargetActor) || !CanRepair())
|
if (!CanRepairAt(order.TargetActor) || (!CanRepair() && !CanRearm()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var movement = self.Trait<IMove>();
|
var movement = self.Trait<IMove>();
|
||||||
@@ -83,7 +97,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new MoveAdjacentTo(self, target));
|
self.QueueActivity(new MoveAdjacentTo(self, target));
|
||||||
self.QueueActivity(movement.MoveTo(self.World.Map.CellContaining(order.TargetActor.CenterPosition), order.TargetActor));
|
self.QueueActivity(movement.MoveTo(self.World.Map.CellContaining(order.TargetActor.CenterPosition), order.TargetActor));
|
||||||
self.QueueActivity(new Rearm(self));
|
if (CanRearmAt(order.TargetActor) && CanRearm())
|
||||||
|
self.QueueActivity(new Rearm(self));
|
||||||
|
|
||||||
self.QueueActivity(new Repair(order.TargetActor));
|
self.QueueActivity(new Repair(order.TargetActor));
|
||||||
|
|
||||||
var rp = order.TargetActor.TraitOrDefault<RallyPoint>();
|
var rp = order.TargetActor.TraitOrDefault<RallyPoint>();
|
||||||
|
|||||||
@@ -774,6 +774,61 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
|||||||
node.Key = "StandSequences";
|
node.Key = "StandSequences";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (engineVersion < 20150323)
|
||||||
|
{
|
||||||
|
// Moved Reloads functionality to LimitedAmmo and refactored the latter into AmmoPool
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
var actorTraits = node.Value.Nodes;
|
||||||
|
var limitedAmmo = actorTraits.FirstOrDefault(l => l.Key == "LimitedAmmo");
|
||||||
|
var reloads = actorTraits.FirstOrDefault(r => r.Key == "Reloads");
|
||||||
|
|
||||||
|
if (reloads != null)
|
||||||
|
{
|
||||||
|
var reloadsFields = reloads.Value.Nodes;
|
||||||
|
var limitedAmmoFields = limitedAmmo.Value.Nodes;
|
||||||
|
var count = reloadsFields.FirstOrDefault(c => c.Key == "Count");
|
||||||
|
var period = reloadsFields.FirstOrDefault(p => p.Key == "Period");
|
||||||
|
var resets = reloadsFields.FirstOrDefault(res => res.Key == "ResetOnFire");
|
||||||
|
|
||||||
|
var reloadsCount = count != null ? FieldLoader.GetValue<int>("Count", count.Value.Value) : -1;
|
||||||
|
var reloadsPeriod = period != null ? FieldLoader.GetValue<int>("Period", period.Value.Value) : 50;
|
||||||
|
var reloadsResetOnFire = resets != null ? FieldLoader.GetValue<bool>("ResetOnFire", resets.Value.Value) : false;
|
||||||
|
|
||||||
|
limitedAmmoFields.Add(new MiniYamlNode("SelfReloads", "true"));
|
||||||
|
limitedAmmoFields.Add(new MiniYamlNode("ReloadCount", reloadsCount.ToString()));
|
||||||
|
limitedAmmoFields.Add(new MiniYamlNode("SelfReloadTicks", reloadsPeriod.ToString()));
|
||||||
|
limitedAmmoFields.Add(new MiniYamlNode("ResetOnFire", reloadsResetOnFire.ToString()));
|
||||||
|
|
||||||
|
node.Value.Nodes.RemoveAll(n => n.Key == "Reloads");
|
||||||
|
node.Value.Nodes.RemoveAll(n => n.Key == "-Reloads");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moved RearmSound from Minelayer to LimitedAmmo/AmmoPool
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
var actorTraits = node.Value.Nodes;
|
||||||
|
var limitedAmmo = actorTraits.FirstOrDefault(la => la.Key == "LimitedAmmo");
|
||||||
|
var minelayer = actorTraits.FirstOrDefault(ml => ml.Key == "Minelayer");
|
||||||
|
|
||||||
|
if (minelayer != null)
|
||||||
|
{
|
||||||
|
var minelayerFields = minelayer.Value.Nodes;
|
||||||
|
var limitedAmmoFields = limitedAmmo.Value.Nodes;
|
||||||
|
var rearmSound = minelayerFields.FirstOrDefault(rs => rs.Key == "RearmSound");
|
||||||
|
var minelayerRearmSound = rearmSound != null ? FieldLoader.GetValue<string>("RearmSound", rearmSound.Value.Value) : "minelay1.aud";
|
||||||
|
|
||||||
|
limitedAmmoFields.Add(new MiniYamlNode("RearmSound", minelayerRearmSound.ToString()));
|
||||||
|
minelayerFields.Remove(rearmSound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename LimitedAmmo to AmmoPool
|
||||||
|
if (node.Key == "LimitedAmmo")
|
||||||
|
node.Key = "AmmoPool";
|
||||||
|
}
|
||||||
|
|
||||||
UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
|
UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Activities;
|
using OpenRA.Activities;
|
||||||
using OpenRA.Mods.Common.Activities;
|
using OpenRA.Mods.Common.Activities;
|
||||||
@@ -18,23 +20,34 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
// assumes you have Minelayer on that unit
|
// Assumes you have Minelayer on that unit
|
||||||
class LayMines : Activity
|
public class LayMines : Activity
|
||||||
{
|
{
|
||||||
|
readonly Minelayer minelayer;
|
||||||
|
readonly MinelayerInfo info;
|
||||||
|
readonly IEnumerable<AmmoPool> ammoPools;
|
||||||
|
readonly IMove movement;
|
||||||
|
readonly string[] rearmBuildings;
|
||||||
|
|
||||||
|
public LayMines(Actor self)
|
||||||
|
{
|
||||||
|
minelayer = self.TraitOrDefault<Minelayer>();
|
||||||
|
info = self.Info.Traits.Get<MinelayerInfo>();
|
||||||
|
ammoPools = self.TraitsImplementing<AmmoPool>();
|
||||||
|
movement = self.Trait<IMove>();
|
||||||
|
rearmBuildings = info.RearmBuildings;
|
||||||
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled) return NextActivity;
|
if (IsCanceled)
|
||||||
|
return NextActivity;
|
||||||
|
|
||||||
var movement = self.Trait<IMove>();
|
if (ammoPools != null && ammoPools.Any(p => p.Info.Name == info.AmmoPoolName && !p.HasAmmo()))
|
||||||
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();
|
|
||||||
if (!limitedAmmo.HasAmmo())
|
|
||||||
{
|
{
|
||||||
var info = self.Info.Traits.Get<MinelayerInfo>();
|
// Rearm (and possibly repair) at rearm building, then back out here to refill the minefield some more
|
||||||
|
|
||||||
// rearm & repair at fix, then back out here to refill the minefield some more
|
|
||||||
var buildings = info.RearmBuildings;
|
|
||||||
var rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally
|
var rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally
|
||||||
&& buildings.Contains(a.Info.Name))
|
&& rearmBuildings.Contains(a.Info.Name))
|
||||||
.ClosestTo(self);
|
.ClosestTo(self);
|
||||||
|
|
||||||
if (rearmTarget == null)
|
if (rearmTarget == null)
|
||||||
@@ -43,47 +56,50 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
return Util.SequenceActivities(
|
return Util.SequenceActivities(
|
||||||
new MoveAdjacentTo(self, Target.FromActor(rearmTarget)),
|
new MoveAdjacentTo(self, Target.FromActor(rearmTarget)),
|
||||||
movement.MoveTo(self.World.Map.CellContaining(rearmTarget.CenterPosition), rearmTarget),
|
movement.MoveTo(self.World.Map.CellContaining(rearmTarget.CenterPosition), rearmTarget),
|
||||||
new Rearm(self, info.RearmSound),
|
new Rearm(self),
|
||||||
new Repair(rearmTarget),
|
new Repair(rearmTarget),
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ml = self.Trait<Minelayer>();
|
if (minelayer.Minefield.Contains(self.Location) && ShouldLayMine(self, self.Location))
|
||||||
if (ml.Minefield.Contains(self.Location) && ShouldLayMine(self, self.Location))
|
|
||||||
{
|
{
|
||||||
LayMine(self);
|
LayMine(self);
|
||||||
return Util.SequenceActivities(new Wait(20), this); // a little wait after placing each mine, for show
|
return Util.SequenceActivities(new Wait(20), this); // A little wait after placing each mine, for show
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ml.Minefield.Length > 0)
|
if (minelayer.Minefield.Length > 0)
|
||||||
{
|
{
|
||||||
// dont get stuck forever here
|
// Don't get stuck forever here
|
||||||
for (var n = 0; n < 20; n++)
|
for (var n = 0; n < 20; n++)
|
||||||
{
|
{
|
||||||
var p = ml.Minefield.Random(self.World.SharedRandom);
|
var p = minelayer.Minefield.Random(self.World.SharedRandom);
|
||||||
if (ShouldLayMine(self, p))
|
if (ShouldLayMine(self, p))
|
||||||
return Util.SequenceActivities(movement.MoveTo(p, 0), this);
|
return Util.SequenceActivities(movement.MoveTo(p, 0), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: return somewhere likely to be safe (near fix) so we're not sitting out in the minefield.
|
// TODO: Return somewhere likely to be safe (near rearm building) so we're not sitting out in the minefield.
|
||||||
return new Wait(20); // nothing to do here
|
return new Wait(20); // nothing to do here
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ShouldLayMine(Actor self, CPos p)
|
static bool ShouldLayMine(Actor self, CPos p)
|
||||||
{
|
{
|
||||||
// if there is no unit (other than me) here, we want to place a mine here
|
// If there is no unit (other than me) here, we want to place a mine here
|
||||||
return !self.World.ActorMap.GetUnitsAt(p).Any(a => a != self);
|
return !self.World.ActorMap.GetUnitsAt(p).Any(a => a != self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LayMine(Actor self)
|
void LayMine(Actor self)
|
||||||
{
|
{
|
||||||
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();
|
if (ammoPools != null)
|
||||||
if (limitedAmmo != null)
|
{
|
||||||
limitedAmmo.TakeAmmo();
|
var pool = ammoPools.FirstOrDefault(x => x.Info.Name == info.AmmoPoolName);
|
||||||
|
if (pool == null)
|
||||||
|
return;
|
||||||
|
pool.TakeAmmo();
|
||||||
|
}
|
||||||
|
|
||||||
self.World.AddFrameEndTask(
|
self.World.AddFrameEndTask(
|
||||||
w => w.CreateActor(self.Info.Traits.Get<MinelayerInfo>().Mine, new TypeDictionary
|
w => w.CreateActor(info.Mine, new TypeDictionary
|
||||||
{
|
{
|
||||||
new LocationInit(self.Location),
|
new LocationInit(self.Location),
|
||||||
new OwnerInit(self.Owner),
|
new OwnerInit(self.Owner),
|
||||||
|
|||||||
@@ -18,19 +18,19 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA.Mods.RA.Traits
|
namespace OpenRA.Mods.RA.Traits
|
||||||
{
|
{
|
||||||
class MinelayerInfo : ITraitInfo
|
public class MinelayerInfo : ITraitInfo
|
||||||
{
|
{
|
||||||
[ActorReference] public readonly string Mine = "minv";
|
[ActorReference] public readonly string Mine = "minv";
|
||||||
[ActorReference] public readonly string[] RearmBuildings = { "fix" };
|
[ActorReference] public readonly string[] RearmBuildings = { "fix" };
|
||||||
|
|
||||||
public readonly string RearmSound = "minelay1.aud";
|
public readonly string AmmoPoolName = "primary";
|
||||||
|
|
||||||
public readonly float MinefieldDepth = 1.5f;
|
public readonly float MinefieldDepth = 1.5f;
|
||||||
|
|
||||||
public object Create(ActorInitializer init) { return new Minelayer(init.Self); }
|
public object Create(ActorInitializer init) { return new Minelayer(init.Self); }
|
||||||
}
|
}
|
||||||
|
|
||||||
class Minelayer : IIssueOrder, IResolveOrder, IPostRenderSelection, ISync
|
public class Minelayer : IIssueOrder, IResolveOrder, IPostRenderSelection, ISync
|
||||||
{
|
{
|
||||||
/* TODO: [Sync] when sync can cope with arrays! */
|
/* TODO: [Sync] when sync can cope with arrays! */
|
||||||
public CPos[] Minefield = null;
|
public CPos[] Minefield = null;
|
||||||
@@ -80,7 +80,7 @@ namespace OpenRA.Mods.RA.Traits
|
|||||||
minefieldStart = order.TargetLocation;
|
minefieldStart = order.TargetLocation;
|
||||||
Minefield = new CPos[] { order.TargetLocation };
|
Minefield = new CPos[] { order.TargetLocation };
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new LayMines());
|
self.QueueActivity(new LayMines(self));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (order.OrderString == "PlaceMinefield")
|
if (order.OrderString == "PlaceMinefield")
|
||||||
@@ -92,7 +92,7 @@ namespace OpenRA.Mods.RA.Traits
|
|||||||
.Where(p => movement.CanEnterCell(p, null, false)).ToArray();
|
.Where(p => movement.CanEnterCell(p, null, false)).ToArray();
|
||||||
|
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new LayMines());
|
self.QueueActivity(new LayMines(self));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,12 +76,12 @@ HELI:
|
|||||||
MuzzleSequence: muzzle
|
MuzzleSequence: muzzle
|
||||||
AttackHeli:
|
AttackHeli:
|
||||||
FacingTolerance: 20
|
FacingTolerance: 20
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 10
|
Ammo: 10
|
||||||
PipCount: 5
|
PipCount: 5
|
||||||
Reloads:
|
SelfReloads: true
|
||||||
Count: 10
|
ReloadCount: 10
|
||||||
Period: 200
|
SelfReloadTicks: 200
|
||||||
RenderUnit:
|
RenderUnit:
|
||||||
WithRotor:
|
WithRotor:
|
||||||
Offset: 0,0,85
|
Offset: 0,0,85
|
||||||
@@ -124,12 +124,12 @@ ORCA:
|
|||||||
LocalOffset: 427,-171,-213, 427,171,-213
|
LocalOffset: 427,-171,-213, 427,171,-213
|
||||||
AttackHeli:
|
AttackHeli:
|
||||||
FacingTolerance: 20
|
FacingTolerance: 20
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 6
|
Ammo: 6
|
||||||
PipCount: 6
|
PipCount: 6
|
||||||
Reloads:
|
SelfReloads: true
|
||||||
Count: 2
|
ReloadCount: 2
|
||||||
Period: 100
|
SelfReloadTicks: 100
|
||||||
RenderUnit:
|
RenderUnit:
|
||||||
LeavesHusk:
|
LeavesHusk:
|
||||||
HuskActor: ORCA.Husk
|
HuskActor: ORCA.Husk
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ orni.bomber:
|
|||||||
RepairBuildings: repair
|
RepairBuildings: repair
|
||||||
RearmBuildings:
|
RearmBuildings:
|
||||||
Repulsable: False
|
Repulsable: False
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 5
|
Ammo: 5
|
||||||
RenderUnit:
|
RenderUnit:
|
||||||
Image: orni
|
Image: orni
|
||||||
|
|||||||
@@ -681,7 +681,7 @@ Rules:
|
|||||||
Plane:
|
Plane:
|
||||||
ROT: 5
|
ROT: 5
|
||||||
Speed: 280
|
Speed: 280
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 30
|
Ammo: 30
|
||||||
RenderUnit:
|
RenderUnit:
|
||||||
Image: mig
|
Image: mig
|
||||||
|
|||||||
@@ -2273,7 +2273,7 @@ Rules:
|
|||||||
MIG:
|
MIG:
|
||||||
Buildable:
|
Buildable:
|
||||||
Prerequisites: ~afld
|
Prerequisites: ~afld
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 2
|
Ammo: 2
|
||||||
HELI:
|
HELI:
|
||||||
Buildable:
|
Buildable:
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ BADR.Bomber:
|
|||||||
Speed: 149
|
Speed: 149
|
||||||
Repulsable: False
|
Repulsable: False
|
||||||
MaximumPitch: 56
|
MaximumPitch: 56
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 7
|
Ammo: 7
|
||||||
RenderUnit:
|
RenderUnit:
|
||||||
Image: badr
|
Image: badr
|
||||||
@@ -113,7 +113,7 @@ MIG:
|
|||||||
EnableStances: false
|
EnableStances: false
|
||||||
RenderUnit:
|
RenderUnit:
|
||||||
CameraPitch: 99
|
CameraPitch: 99
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 8
|
Ammo: 8
|
||||||
ReturnOnIdle:
|
ReturnOnIdle:
|
||||||
Selectable:
|
Selectable:
|
||||||
@@ -171,7 +171,7 @@ YAK:
|
|||||||
EnableStances: false
|
EnableStances: false
|
||||||
RenderUnit:
|
RenderUnit:
|
||||||
CameraPitch: 99
|
CameraPitch: 99
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 18
|
Ammo: 18
|
||||||
PipCount: 6
|
PipCount: 6
|
||||||
ReloadTicks: 11
|
ReloadTicks: 11
|
||||||
@@ -264,7 +264,7 @@ HELI:
|
|||||||
RenderUnit:
|
RenderUnit:
|
||||||
WithRotor:
|
WithRotor:
|
||||||
Offset: 0,0,85
|
Offset: 0,0,85
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 8
|
Ammo: 8
|
||||||
Selectable:
|
Selectable:
|
||||||
Bounds: 36,28,0,0
|
Bounds: 36,28,0,0
|
||||||
@@ -312,7 +312,7 @@ HIND:
|
|||||||
InitialStance: HoldFire
|
InitialStance: HoldFire
|
||||||
RenderUnit:
|
RenderUnit:
|
||||||
WithRotor:
|
WithRotor:
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 24
|
Ammo: 24
|
||||||
PipCount: 6
|
PipCount: 6
|
||||||
ReloadTicks: 8
|
ReloadTicks: 8
|
||||||
|
|||||||
@@ -419,8 +419,9 @@ MNLY.AP:
|
|||||||
Minelayer:
|
Minelayer:
|
||||||
Mine: MINP
|
Mine: MINP
|
||||||
MineImmune:
|
MineImmune:
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 5
|
Ammo: 5
|
||||||
|
RearmSound: minelay1.aud
|
||||||
DetectCloaked:
|
DetectCloaked:
|
||||||
Range: 5
|
Range: 5
|
||||||
CloakTypes: Mine
|
CloakTypes: Mine
|
||||||
@@ -453,8 +454,9 @@ MNLY.AT:
|
|||||||
Minelayer:
|
Minelayer:
|
||||||
Mine: MINV
|
Mine: MINV
|
||||||
MineImmune:
|
MineImmune:
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 5
|
Ammo: 5
|
||||||
|
RearmSound: minelay1.aud
|
||||||
DetectCloaked:
|
DetectCloaked:
|
||||||
Range: 5
|
Range: 5
|
||||||
CloakTypes: Mine
|
CloakTypes: Mine
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ DPOD:
|
|||||||
Armament:
|
Armament:
|
||||||
Weapon: Vulcan2
|
Weapon: Vulcan2
|
||||||
AttackHeli:
|
AttackHeli:
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 5
|
Ammo: 5
|
||||||
PipCount: 5
|
PipCount: 5
|
||||||
PipType: Ammo
|
PipType: Ammo
|
||||||
@@ -95,7 +95,7 @@ ORCA:
|
|||||||
Weapon: Hellfire
|
Weapon: Hellfire
|
||||||
AttackHeli:
|
AttackHeli:
|
||||||
FacingTolerance: 20
|
FacingTolerance: 20
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 5
|
Ammo: 5
|
||||||
PipCount: 5
|
PipCount: 5
|
||||||
PipType: Ammo
|
PipType: Ammo
|
||||||
@@ -132,7 +132,7 @@ ORCAB:
|
|||||||
Weapon: Bomb
|
Weapon: Bomb
|
||||||
AttackHeli:
|
AttackHeli:
|
||||||
FacingTolerance: 20
|
FacingTolerance: 20
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 2
|
Ammo: 2
|
||||||
PipCount: 2
|
PipCount: 2
|
||||||
PipType: Ammo
|
PipType: Ammo
|
||||||
@@ -228,7 +228,7 @@ SCRIN:
|
|||||||
Weapon: Proton
|
Weapon: Proton
|
||||||
AttackHeli:
|
AttackHeli:
|
||||||
FacingTolerance: 20
|
FacingTolerance: 20
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 3
|
Ammo: 3
|
||||||
PipCount: 3
|
PipCount: 3
|
||||||
PipType: Ammo
|
PipType: Ammo
|
||||||
@@ -264,7 +264,7 @@ APACHE:
|
|||||||
Weapon: HarpyClaw
|
Weapon: HarpyClaw
|
||||||
AttackHeli:
|
AttackHeli:
|
||||||
FacingTolerance: 20
|
FacingTolerance: 20
|
||||||
LimitedAmmo:
|
AmmoPool:
|
||||||
Ammo: 12
|
Ammo: 12
|
||||||
PipCount: 4
|
PipCount: 4
|
||||||
PipType: Ammo
|
PipType: Ammo
|
||||||
|
|||||||
Reference in New Issue
Block a user