Rewrite Enter and related activities.

The unit will now path to the nearest adjacent cell, drag inside, do the inner activity, then (if still alive) drag back to its original
location.

This fixes:
 - Mismatch in logic between Enter and MoveAdjacentTo,
   which causes an infinite loop.
 - Building capturing failing from certain directions.
 - Being unable to enter buildings on unpathable tiles.
 - Units being stranded inside a building if the requirements
   for the inner order aren't met.
This commit is contained in:
Paul Chote
2013-04-15 01:50:19 +12:00
parent 8676562d47
commit e76c746b61
10 changed files with 87 additions and 82 deletions

View File

@@ -16,37 +16,35 @@ namespace OpenRA.Mods.RA.Activities
{
class Demolish : Activity
{
Actor target;
Target target;
int delay;
public Demolish( Actor target, int delay )
public Demolish(Actor target, int delay)
{
this.target = target;
this.target = Target.FromActor(target);
this.delay = delay;
}
public override Activity Tick(Actor self)
{
if (IsCanceled) return NextActivity;
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
if( !target.OccupiesSpace.OccupiedCells().Any( x => x.First == self.Location ) )
if (IsCanceled || !target.IsValid)
return NextActivity;
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
{
// Can't demolish an already dead actor
if (target.IsDead())
if (!target.IsValid)
return;
// Invulnerable actors can't be demolished
var modifier = (float)target.TraitsImplementing<IDamageModifier>()
var modifier = (float)target.Actor.TraitsImplementing<IDamageModifier>()
.Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
.Select(t => t.GetDamageModifier(self, null)).Product();
if (target.IsInWorld && modifier > 0)
target.Kill(self);
if (modifier > 0)
target.Actor.Kill(self);
})));
return NextActivity;
}
}

View File

@@ -16,26 +16,26 @@ namespace OpenRA.Mods.RA.Activities
{
class DonateSupplies : Activity
{
Actor target;
Target target;
int payload;
public DonateSupplies(Actor target, int payload)
{
this.target = target;
this.target = Target.FromActor(target);
this.payload = payload;
}
public override Activity Tick(Actor self)
{
if (IsCanceled) return NextActivity;
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
if (!target.OccupiesSpace.OccupiedCells().Any(x => x.First == self.Location))
if (IsCanceled || !target.IsValid)
return NextActivity;
target.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(payload);
var targetPlayer = target.Actor.Owner;
targetPlayer.PlayerActor.Trait<PlayerResources>().GiveCash(payload);
self.Destroy();
if (self.World.LocalPlayer == null || self.Owner.Stances[self.World.LocalPlayer] == Stance.Ally)
self.World.AddFrameEndTask(w => w.Add(new CashTick(payload, 30, 2, target.CenterLocation, target.Owner.ColorRamp.GetColor(0))));
if (self.Owner.IsAlliedWith(self.World.RenderPlayer))
self.World.AddFrameEndTask(w => w.Add(new CashTick(payload, 30, 2, target.CenterLocation, targetPlayer.ColorRamp.GetColor(0))));
return this;
}

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System.Linq;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
@@ -15,20 +16,38 @@ namespace OpenRA.Mods.RA.Activities
{
public class Enter : Activity
{
readonly Actor target;
public Enter( Actor target ) { this.target = target; }
readonly Target target;
readonly Activity inner;
public override Activity Tick( Actor self )
public Enter(Actor target, Activity inner)
{
if( IsCanceled || target.Destroyed || !target.IsInWorld )
this.target = Target.FromActor(target);
this.inner = inner;
}
public override Activity Tick(Actor self)
{
if (IsCanceled || !target.IsValid)
return NextActivity;
var mobile = self.Trait<Mobile>();
var nearest = target.OccupiesSpace.NearestCellTo( mobile.toCell );
if( ( nearest - mobile.toCell ).LengthSquared > 2 )
return Util.SequenceActivities( new MoveAdjacentTo( Target.FromActor(target) ), this );
if (!Util.AdjacentCells(target).Any(c => c == self.Location))
return Util.SequenceActivities(new MoveAdjacentTo(target), this);
return Util.SequenceActivities( mobile.MoveTo( nearest, target ), NextActivity );
// Move to the middle of the target, ignoring impassable tiles
var mobile = self.Trait<Mobile>();
var to = target.CenterLocation;
var from = self.CenterLocation;
var speed = mobile.MovementSpeedForCell(self, self.Location);
var length = speed > 0 ? (int)((to - from).Length * 3 / speed) : 0;
return Util.SequenceActivities(
new Turn(Util.GetFacing(to - from, mobile.Facing)),
new Drag(from, to, length),
inner,
new Turn(Util.GetFacing(from - to, mobile.Facing)),
new Drag(to, from, length),
NextActivity
);
}
}
}

View File

@@ -16,31 +16,23 @@ namespace OpenRA.Mods.RA.Activities
{
class Infiltrate : Activity
{
Actor target;
public Infiltrate(Actor target) { this.target = target; }
Target target;
public Infiltrate(Actor target) { this.target = Target.FromActor(target); }
public override Activity Tick(Actor self)
{
if (IsCanceled) return NextActivity;
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
if (target.Owner == self.Owner) return NextActivity;
if( !target.OccupiesSpace.OccupiedCells().Any( x => x.First == self.Location ) )
if (IsCanceled || !target.IsValid || target.Actor.Owner == self.Owner)
return NextActivity;
foreach (var t in target.TraitsImplementing<IAcceptInfiltrator>())
t.OnInfiltrate(target, self);
foreach (var t in target.Actor.TraitsImplementing<IAcceptInfiltrator>())
t.OnInfiltrate(target.Actor, self);
if (self.HasTrait<DontDestroyWhenInfiltrating>())
self.World.AddFrameEndTask(w =>
{
if (self.Destroyed) return;
w.Remove(self);
});
self.World.AddFrameEndTask(w => { if (!self.Destroyed) w.Remove(self); });
else
self.Destroy();
if (target.HasTrait<Building>())
if (target.Actor.HasTrait<Building>())
Sound.PlayToPlayer(self.Owner, "bldginf1.aud");
return this;

View File

@@ -17,33 +17,34 @@ namespace OpenRA.Mods.RA.Activities
{
readonly Target target;
public MoveAdjacentTo( Target target ) { this.target = target; }
public MoveAdjacentTo(Target target) { this.target = target; }
public override Activity Tick( Actor self )
public override Activity Tick(Actor self)
{
if( IsCanceled || !target.IsValid) return NextActivity;
if (IsCanceled || !target.IsValid)
return NextActivity;
var mobile = self.Trait<Mobile>();
var ps1 = new PathSearch( self.World, mobile.Info, self )
var ps1 = new PathSearch(self.World, mobile.Info, self)
{
checkForBlocked = true,
heuristic = location => 0,
inReverse = true
};
foreach( var cell in Util.AdjacentCells(target) )
foreach (var cell in Util.AdjacentCells(target))
{
if (cell == self.Location)
return NextActivity;
else
ps1.AddInitialCell( cell );
ps1.AddInitialCell(cell);
}
ps1.heuristic = PathSearch.DefaultEstimator( mobile.toCell );
ps1.heuristic = PathSearch.DefaultEstimator(mobile.toCell);
var ps2 = PathSearch.FromPoint(self.World, mobile.Info, self, mobile.toCell, target.CenterLocation.ToCPos(), true);
var ret = self.World.WorldActor.Trait<PathFinder>().FindBidiPath(ps1, ps2);
var ps2 = PathSearch.FromPoint( self.World, mobile.Info, self, mobile.toCell, target.CenterLocation.ToCPos(), true );
var ret = self.World.WorldActor.Trait<PathFinder>().FindBidiPath( ps1, ps2 );
return Util.SequenceActivities( mobile.MoveTo( () => ret ), this );
return Util.SequenceActivities(mobile.MoveTo(() => ret), this);
}
}
}

View File

@@ -15,22 +15,20 @@ namespace OpenRA.Mods.RA.Activities
{
class RepairBuilding : Activity
{
Actor target;
Target target;
public RepairBuilding(Actor target) { this.target = target; }
public RepairBuilding(Actor target) { this.target = Target.FromActor(target); }
public override Activity Tick(Actor self)
{
if (IsCanceled) return NextActivity;
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
if( !target.OccupiesSpace.OccupiedCells().Any( x => x.First == self.Location ) )
if (IsCanceled || !target.IsValid)
return NextActivity;
var health = target.Trait<Health>();
var health = target.Actor.Trait<Health>();
if (health.DamageState == DamageState.Undamaged)
return NextActivity;
target.InflictDamage(self, -health.MaxHP, null);
target.Actor.InflictDamage(self, -health.MaxHP, null);
self.Destroy();
return this;

View File

@@ -52,11 +52,8 @@ namespace OpenRA.Mods.RA
{
self.SetTargetLine(Target.FromOrder(order), Color.Red);
var mobile = self.Trait<Mobile>();
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor));
self.QueueActivity(new Demolish(order.TargetActor, Info.C4Delay));
self.QueueActivity(mobile.MoveTo(self.Location, 0));
self.QueueActivity(new Enter(order.TargetActor, new Demolish(order.TargetActor, Info.C4Delay)));
}
}

View File

@@ -26,9 +26,9 @@ namespace OpenRA.Mods.RA
get { yield return new EngineerRepairOrderTargeter(); }
}
public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued )
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if( order.OrderID == "EngineerRepair" )
if (order.OrderID == "EngineerRepair")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
return null;
@@ -36,41 +36,43 @@ namespace OpenRA.Mods.RA
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "EngineerRepair"
&& order.TargetActor.GetDamageState() > DamageState.Undamaged) ? "Attack" : null;
return (order.OrderString == "EngineerRepair" &&
order.TargetActor.GetDamageState() > DamageState.Undamaged) ? "Attack" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "EngineerRepair"
&& order.TargetActor.GetDamageState() > DamageState.Undamaged)
if (order.OrderString == "EngineerRepair" &&
order.TargetActor.GetDamageState() > DamageState.Undamaged)
{
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor));
self.QueueActivity(new RepairBuilding(order.TargetActor));
self.QueueActivity(new Enter(order.TargetActor, new RepairBuilding(order.TargetActor)));
}
}
class EngineerRepairOrderTargeter : UnitTraitOrderTargeter<Building>
{
public EngineerRepairOrderTargeter()
: base( "EngineerRepair", 6, "goldwrench", false, true ) { }
: base("EngineerRepair", 6, "goldwrench", false, true) { }
public override bool CanTargetActor(Actor self, Actor target, bool forceAttack, bool forceQueued, ref string cursor)
{
if( !base.CanTargetActor( self, target, forceAttack, forceQueued, ref cursor ) ) return false;
if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor))
return false;
if (!target.HasTrait<EngineerRepairable>())
return false;
if (self.Owner.Stances[ target.Owner ] != Stance.Ally)
if (self.Owner.Stances[target.Owner] != Stance.Ally)
return false;
IsQueued = forceQueued;
if( target.GetDamageState() == DamageState.Undamaged )
if (target.GetDamageState() == DamageState.Undamaged)
cursor = "goldwrench-blocked";
return true;
}
}

View File

@@ -67,8 +67,7 @@ namespace OpenRA.Mods.RA
self.SetTargetLine(Target.FromOrder(order), Color.Red);
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor));
self.QueueActivity(new Infiltrate(order.TargetActor));
self.QueueActivity(new Enter(order.TargetActor, new Infiltrate(order.TargetActor)));
}
}

View File

@@ -59,8 +59,7 @@ namespace OpenRA.Mods.RA
{
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor));
self.QueueActivity(new DonateSupplies(order.TargetActor, Info.Payload));
self.QueueActivity(new Enter(order.TargetActor, new DonateSupplies(order.TargetActor, Info.Payload)));
}
}