@@ -26,6 +26,9 @@ namespace OpenRA.Mods.RA
|
||||
public readonly string Cursor = "attack";
|
||||
public readonly string OutsideRangeCursor = "attackoutsiderange";
|
||||
|
||||
[Desc("Does the attack type require the attacker to enter the target's cell?")]
|
||||
public readonly bool AttackRequiresEnteringCell = false;
|
||||
|
||||
public abstract object Create(ActorInitializer init);
|
||||
}
|
||||
|
||||
@@ -35,15 +38,16 @@ namespace OpenRA.Mods.RA
|
||||
public IEnumerable<Armament> Armaments { get { return GetArmaments(); } }
|
||||
protected Lazy<IFacing> facing;
|
||||
protected Lazy<Building> building;
|
||||
protected Lazy<IPositionable> positionable;
|
||||
protected Func<IEnumerable<Armament>> GetArmaments;
|
||||
|
||||
readonly Actor self;
|
||||
readonly AttackBaseInfo info;
|
||||
public readonly AttackBaseInfo Info;
|
||||
|
||||
public AttackBase(Actor self, AttackBaseInfo info)
|
||||
{
|
||||
this.self = self;
|
||||
this.info = info;
|
||||
Info = info;
|
||||
|
||||
var armaments = Exts.Lazy(() => self.TraitsImplementing<Armament>()
|
||||
.Where(a => info.Armaments.Contains(a.Info.Name)));
|
||||
@@ -52,6 +56,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
facing = Exts.Lazy(() => self.TraitOrDefault<IFacing>());
|
||||
building = Exts.Lazy(() => self.TraitOrDefault<Building>());
|
||||
positionable = Exts.Lazy(() => self.Trait<IPositionable>());
|
||||
}
|
||||
|
||||
protected virtual bool CanAttack(Actor self, Target target)
|
||||
@@ -59,6 +64,9 @@ namespace OpenRA.Mods.RA
|
||||
if (!self.IsInWorld)
|
||||
return false;
|
||||
|
||||
if (!HasAnyValidWeapons(target))
|
||||
return false;
|
||||
|
||||
// Building is under construction or is being sold
|
||||
if (building.Value != null && !building.Value.BuildComplete)
|
||||
return false;
|
||||
@@ -135,7 +143,17 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public abstract Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove);
|
||||
|
||||
public bool HasAnyValidWeapons(Target t) { return Armaments.Any(a => a.Weapon.IsValidAgainst(t, self.World, self)); }
|
||||
public bool HasAnyValidWeapons(Target t)
|
||||
{
|
||||
if (Info.AttackRequiresEnteringCell)
|
||||
{
|
||||
if (!positionable.Value.CanEnterCell(t.Actor.Location, null, false))
|
||||
return false;
|
||||
}
|
||||
|
||||
return Armaments.Any(a => a.Weapon.IsValidAgainst(t, self.World, self));
|
||||
}
|
||||
|
||||
public WRange GetMaximumRange()
|
||||
{
|
||||
return Armaments.Select(a => a.Weapon.Range).Append(WRange.Zero).Max();
|
||||
@@ -179,8 +197,8 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
var a = ab.ChooseArmamentForTarget(target);
|
||||
cursor = a != null && !target.IsInRange(self.CenterPosition, a.Weapon.Range)
|
||||
? ab.info.OutsideRangeCursor
|
||||
: ab.info.Cursor;
|
||||
? ab.Info.OutsideRangeCursor
|
||||
: ab.Info.Cursor;
|
||||
|
||||
if (target.Type == TargetType.Actor && target.Actor == self)
|
||||
return false;
|
||||
@@ -210,7 +228,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
|
||||
|
||||
cursor = ab.info.Cursor;
|
||||
cursor = ab.Info.Cursor;
|
||||
|
||||
if (negativeDamage)
|
||||
return false;
|
||||
@@ -223,7 +241,7 @@ namespace OpenRA.Mods.RA
|
||||
var maxRange = ab.GetMaximumRange().Range;
|
||||
var targetRange = (self.World.Map.CenterOfCell(location) - self.CenterPosition).HorizontalLengthSquared;
|
||||
if (targetRange > maxRange * maxRange)
|
||||
cursor = ab.info.OutsideRangeCursor;
|
||||
cursor = ab.Info.OutsideRangeCursor;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -15,25 +15,48 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
[Desc("Will AttackMove to a random location within MoveRadius when idle.",
|
||||
"This conflicts with player orders and should only be added to animal creeps.")]
|
||||
class AttackWanderInfo : ITraitInfo
|
||||
class AttackWanderInfo : ITraitInfo, Requires<AttackMoveInfo>
|
||||
{
|
||||
public readonly int MoveRadius = 4;
|
||||
public readonly int WanderMoveRadius = 10;
|
||||
|
||||
[Desc("Number of ticks to wait until decreasing the effective move radius.")]
|
||||
public readonly int MoveReductionRadiusScale = 5;
|
||||
|
||||
public object Create(ActorInitializer init) { return new AttackWander(init.self, this); }
|
||||
}
|
||||
|
||||
class AttackWander : INotifyIdle
|
||||
{
|
||||
int ticksIdle;
|
||||
int effectiveMoveRadius;
|
||||
readonly AttackMove attackMove;
|
||||
readonly AttackWanderInfo Info;
|
||||
|
||||
public AttackWander(Actor self, AttackWanderInfo info)
|
||||
{
|
||||
Info = info;
|
||||
effectiveMoveRadius = info.WanderMoveRadius;
|
||||
attackMove = self.TraitOrDefault<AttackMove>();
|
||||
}
|
||||
|
||||
public void TickIdle(Actor self)
|
||||
{
|
||||
var target = self.CenterPosition + new WVec(0, -1024*Info.MoveRadius, 0).Rotate(WRot.FromFacing(self.World.SharedRandom.Next(255)));
|
||||
self.Trait<AttackMove>().ResolveOrder(self, new Order("AttackMove", self, false) { TargetLocation = self.World.Map.CellContaining(target) });
|
||||
var target = self.CenterPosition + new WVec(0, -1024 * effectiveMoveRadius, 0).Rotate(WRot.FromFacing(self.World.SharedRandom.Next(255)));
|
||||
var targetCell = self.World.Map.CellContaining(target);
|
||||
|
||||
if (!self.World.Map.Contains(targetCell))
|
||||
{
|
||||
// If MoveRadius is too big there might not be a valid cell to order the attack to (if actor is on a small island and can't leave)
|
||||
if (++ticksIdle % Info.MoveReductionRadiusScale == 0)
|
||||
effectiveMoveRadius--;
|
||||
|
||||
return; // We'll be back the next tick; better to sit idle for a few seconds than prolongue this tick indefinitely with a loop
|
||||
}
|
||||
|
||||
attackMove.ResolveOrder(self, new Order("AttackMove", self, false) { TargetLocation = targetCell });
|
||||
|
||||
ticksIdle = 0;
|
||||
effectiveMoveRadius = Info.WanderMoveRadius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ namespace OpenRA.Mods.RA.Traits
|
||||
|
||||
public void TickIdle(Actor self)
|
||||
{
|
||||
if (TargetLocation.HasValue)
|
||||
// This might cause the actor to be stuck if the target location is unreachable
|
||||
if (TargetLocation.HasValue && self.Location != TargetLocation.Value)
|
||||
Activate(self);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user