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
|
class Demolish : Activity
|
||||||
{
|
{
|
||||||
Actor target;
|
Target target;
|
||||||
int delay;
|
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;
|
this.delay = delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled) return NextActivity;
|
if (IsCanceled || !target.IsValid)
|
||||||
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
|
|
||||||
|
|
||||||
if( !target.OccupiesSpace.OccupiedCells().Any( x => x.First == self.Location ) )
|
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
|
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
|
||||||
{
|
{
|
||||||
// Can't demolish an already dead actor
|
// Can't demolish an already dead actor
|
||||||
if (target.IsDead())
|
if (!target.IsValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Invulnerable actors can't be demolished
|
// 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>())
|
.Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
|
||||||
.Select(t => t.GetDamageModifier(self, null)).Product();
|
.Select(t => t.GetDamageModifier(self, null)).Product();
|
||||||
|
|
||||||
if (target.IsInWorld && modifier > 0)
|
if (modifier > 0)
|
||||||
target.Kill(self);
|
target.Actor.Kill(self);
|
||||||
})));
|
})));
|
||||||
|
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,26 +16,26 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
{
|
{
|
||||||
class DonateSupplies : Activity
|
class DonateSupplies : Activity
|
||||||
{
|
{
|
||||||
Actor target;
|
Target target;
|
||||||
int payload;
|
int payload;
|
||||||
|
|
||||||
public DonateSupplies(Actor target, int payload)
|
public DonateSupplies(Actor target, int payload)
|
||||||
{
|
{
|
||||||
this.target = target;
|
this.target = Target.FromActor(target);
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled) return NextActivity;
|
if (IsCanceled || !target.IsValid)
|
||||||
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
|
|
||||||
if (!target.OccupiesSpace.OccupiedCells().Any(x => x.First == self.Location))
|
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
target.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(payload);
|
var targetPlayer = target.Actor.Owner;
|
||||||
|
targetPlayer.PlayerActor.Trait<PlayerResources>().GiveCash(payload);
|
||||||
self.Destroy();
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using OpenRA.Mods.RA.Move;
|
using OpenRA.Mods.RA.Move;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
@@ -15,20 +16,38 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
{
|
{
|
||||||
public class Enter : Activity
|
public class Enter : Activity
|
||||||
{
|
{
|
||||||
readonly Actor target;
|
readonly Target target;
|
||||||
public Enter( Actor target ) { this.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;
|
return NextActivity;
|
||||||
|
|
||||||
var mobile = self.Trait<Mobile>();
|
if (!Util.AdjacentCells(target).Any(c => c == self.Location))
|
||||||
var nearest = target.OccupiesSpace.NearestCellTo( mobile.toCell );
|
return Util.SequenceActivities(new MoveAdjacentTo(target), this);
|
||||||
if( ( nearest - mobile.toCell ).LengthSquared > 2 )
|
|
||||||
return Util.SequenceActivities( new MoveAdjacentTo( Target.FromActor(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
|
class Infiltrate : Activity
|
||||||
{
|
{
|
||||||
Actor target;
|
Target target;
|
||||||
public Infiltrate(Actor target) { this.target = target; }
|
public Infiltrate(Actor target) { this.target = Target.FromActor(target); }
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled) return NextActivity;
|
if (IsCanceled || !target.IsValid || target.Actor.Owner == self.Owner)
|
||||||
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 ) )
|
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
foreach (var t in target.TraitsImplementing<IAcceptInfiltrator>())
|
foreach (var t in target.Actor.TraitsImplementing<IAcceptInfiltrator>())
|
||||||
t.OnInfiltrate(target, self);
|
t.OnInfiltrate(target.Actor, self);
|
||||||
|
|
||||||
if (self.HasTrait<DontDestroyWhenInfiltrating>())
|
if (self.HasTrait<DontDestroyWhenInfiltrating>())
|
||||||
self.World.AddFrameEndTask(w =>
|
self.World.AddFrameEndTask(w => { if (!self.Destroyed) w.Remove(self); });
|
||||||
{
|
|
||||||
if (self.Destroyed) return;
|
|
||||||
w.Remove(self);
|
|
||||||
});
|
|
||||||
else
|
else
|
||||||
self.Destroy();
|
self.Destroy();
|
||||||
|
|
||||||
if (target.HasTrait<Building>())
|
if (target.Actor.HasTrait<Building>())
|
||||||
Sound.PlayToPlayer(self.Owner, "bldginf1.aud");
|
Sound.PlayToPlayer(self.Owner, "bldginf1.aud");
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -17,33 +17,34 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
{
|
{
|
||||||
readonly Target target;
|
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 mobile = self.Trait<Mobile>();
|
||||||
|
var ps1 = new PathSearch(self.World, mobile.Info, self)
|
||||||
var ps1 = new PathSearch( self.World, mobile.Info, self )
|
|
||||||
{
|
{
|
||||||
checkForBlocked = true,
|
checkForBlocked = true,
|
||||||
heuristic = location => 0,
|
heuristic = location => 0,
|
||||||
inReverse = true
|
inReverse = true
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach( var cell in Util.AdjacentCells(target) )
|
foreach (var cell in Util.AdjacentCells(target))
|
||||||
|
{
|
||||||
if (cell == self.Location)
|
if (cell == self.Location)
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
else
|
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 );
|
return Util.SequenceActivities(mobile.MoveTo(() => ret), this);
|
||||||
var ret = self.World.WorldActor.Trait<PathFinder>().FindBidiPath( ps1, ps2 );
|
|
||||||
|
|
||||||
return Util.SequenceActivities( mobile.MoveTo( () => ret ), this );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,22 +15,20 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
{
|
{
|
||||||
class RepairBuilding : Activity
|
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)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled) return NextActivity;
|
if (IsCanceled || !target.IsValid)
|
||||||
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
|
|
||||||
if( !target.OccupiesSpace.OccupiedCells().Any( x => x.First == self.Location ) )
|
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
var health = target.Trait<Health>();
|
var health = target.Actor.Trait<Health>();
|
||||||
if (health.DamageState == DamageState.Undamaged)
|
if (health.DamageState == DamageState.Undamaged)
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
target.InflictDamage(self, -health.MaxHP, null);
|
target.Actor.InflictDamage(self, -health.MaxHP, null);
|
||||||
self.Destroy();
|
self.Destroy();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -52,11 +52,8 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
self.SetTargetLine(Target.FromOrder(order), Color.Red);
|
self.SetTargetLine(Target.FromOrder(order), Color.Red);
|
||||||
|
|
||||||
var mobile = self.Trait<Mobile>();
|
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new Enter(order.TargetActor));
|
self.QueueActivity(new Enter(order.TargetActor, new Demolish(order.TargetActor, Info.C4Delay)));
|
||||||
self.QueueActivity(new Demolish(order.TargetActor, Info.C4Delay));
|
|
||||||
self.QueueActivity(mobile.MoveTo(self.Location, 0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ namespace OpenRA.Mods.RA
|
|||||||
get { yield return new EngineerRepairOrderTargeter(); }
|
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 new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -36,41 +36,43 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public string VoicePhraseForOrder(Actor self, Order order)
|
public string VoicePhraseForOrder(Actor self, Order order)
|
||||||
{
|
{
|
||||||
return (order.OrderString == "EngineerRepair"
|
return (order.OrderString == "EngineerRepair" &&
|
||||||
&& order.TargetActor.GetDamageState() > DamageState.Undamaged) ? "Attack" : null;
|
order.TargetActor.GetDamageState() > DamageState.Undamaged) ? "Attack" : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResolveOrder(Actor self, Order order)
|
public void ResolveOrder(Actor self, Order order)
|
||||||
{
|
{
|
||||||
if (order.OrderString == "EngineerRepair"
|
if (order.OrderString == "EngineerRepair" &&
|
||||||
&& order.TargetActor.GetDamageState() > DamageState.Undamaged)
|
order.TargetActor.GetDamageState() > DamageState.Undamaged)
|
||||||
{
|
{
|
||||||
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
|
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
|
||||||
|
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new Enter(order.TargetActor));
|
self.QueueActivity(new Enter(order.TargetActor, new RepairBuilding(order.TargetActor)));
|
||||||
self.QueueActivity(new RepairBuilding(order.TargetActor));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EngineerRepairOrderTargeter : UnitTraitOrderTargeter<Building>
|
class EngineerRepairOrderTargeter : UnitTraitOrderTargeter<Building>
|
||||||
{
|
{
|
||||||
public EngineerRepairOrderTargeter()
|
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)
|
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>())
|
if (!target.HasTrait<EngineerRepairable>())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (self.Owner.Stances[ target.Owner ] != Stance.Ally)
|
if (self.Owner.Stances[target.Owner] != Stance.Ally)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IsQueued = forceQueued;
|
IsQueued = forceQueued;
|
||||||
|
|
||||||
if( target.GetDamageState() == DamageState.Undamaged )
|
if (target.GetDamageState() == DamageState.Undamaged)
|
||||||
cursor = "goldwrench-blocked";
|
cursor = "goldwrench-blocked";
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,8 +67,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.SetTargetLine(Target.FromOrder(order), Color.Red);
|
self.SetTargetLine(Target.FromOrder(order), Color.Red);
|
||||||
|
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new Enter(order.TargetActor));
|
self.QueueActivity(new Enter(order.TargetActor, new Infiltrate(order.TargetActor)));
|
||||||
self.QueueActivity(new Infiltrate(order.TargetActor));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,8 +59,7 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
|
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new Enter(order.TargetActor));
|
self.QueueActivity(new Enter(order.TargetActor, new DonateSupplies(order.TargetActor, Info.Payload)));
|
||||||
self.QueueActivity(new DonateSupplies(order.TargetActor, Info.Payload));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user