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:
@@ -16,37 +16,35 @@ namespace OpenRA.Mods.RA.Activities
|
||||
{
|
||||
class Demolish : Activity
|
||||
{
|
||||
Actor target;
|
||||
Target 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 Enter(Actor target, Activity inner)
|
||||
{
|
||||
this.target = Target.FromActor(target);
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if( IsCanceled || target.Destroyed || !target.IsInWorld )
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -21,10 +21,10 @@ namespace OpenRA.Mods.RA.Activities
|
||||
|
||||
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)
|
||||
{
|
||||
checkForBlocked = true,
|
||||
@@ -33,13 +33,14 @@ namespace OpenRA.Mods.RA.Activities
|
||||
};
|
||||
|
||||
foreach (var cell in Util.AdjacentCells(target))
|
||||
{
|
||||
if (cell == self.Location)
|
||||
return NextActivity;
|
||||
else
|
||||
ps1.AddInitialCell(cell);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,20 +36,19 @@ 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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +59,9 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
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;
|
||||
|
||||
@@ -71,6 +72,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
if (target.GetDamageState() == DamageState.Undamaged)
|
||||
cursor = "goldwrench-blocked";
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user