From e76c746b61cc239894e20ae050a099ea98f14910 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 15 Apr 2013 01:50:19 +1200 Subject: [PATCH] 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. --- OpenRA.Mods.RA/Activities/Demolish.cs | 20 +++++------ OpenRA.Mods.RA/Activities/DonateSupplies.cs | 16 ++++----- OpenRA.Mods.RA/Activities/Enter.cs | 37 ++++++++++++++++----- OpenRA.Mods.RA/Activities/Infiltrate.cs | 22 ++++-------- OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs | 25 +++++++------- OpenRA.Mods.RA/Activities/RepairBuilding.cs | 12 +++---- OpenRA.Mods.RA/C4Demolition.cs | 5 +-- OpenRA.Mods.RA/EngineerRepair.cs | 26 ++++++++------- OpenRA.Mods.RA/Infiltrates.cs | 3 +- OpenRA.Mods.RA/SupplyTruck.cs | 3 +- 10 files changed, 87 insertions(+), 82 deletions(-) diff --git a/OpenRA.Mods.RA/Activities/Demolish.cs b/OpenRA.Mods.RA/Activities/Demolish.cs index d42ccd8cc9..2d9a9e0b26 100644 --- a/OpenRA.Mods.RA/Activities/Demolish.cs +++ b/OpenRA.Mods.RA/Activities/Demolish.cs @@ -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() + var modifier = (float)target.Actor.TraitsImplementing() .Concat(self.Owner.PlayerActor.TraitsImplementing()) .Select(t => t.GetDamageModifier(self, null)).Product(); - if (target.IsInWorld && modifier > 0) - target.Kill(self); + if (modifier > 0) + target.Actor.Kill(self); }))); + return NextActivity; } } diff --git a/OpenRA.Mods.RA/Activities/DonateSupplies.cs b/OpenRA.Mods.RA/Activities/DonateSupplies.cs index ea9d1e54f4..7a805cc826 100644 --- a/OpenRA.Mods.RA/Activities/DonateSupplies.cs +++ b/OpenRA.Mods.RA/Activities/DonateSupplies.cs @@ -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().GiveCash(payload); + var targetPlayer = target.Actor.Owner; + targetPlayer.PlayerActor.Trait().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; } diff --git a/OpenRA.Mods.RA/Activities/Enter.cs b/OpenRA.Mods.RA/Activities/Enter.cs index 75c6c27d3b..652e970c63 100755 --- a/OpenRA.Mods.RA/Activities/Enter.cs +++ b/OpenRA.Mods.RA/Activities/Enter.cs @@ -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(); - 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(); + 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 + ); } } } diff --git a/OpenRA.Mods.RA/Activities/Infiltrate.cs b/OpenRA.Mods.RA/Activities/Infiltrate.cs index b5d82cd3cc..fc9c9cdbdd 100644 --- a/OpenRA.Mods.RA/Activities/Infiltrate.cs +++ b/OpenRA.Mods.RA/Activities/Infiltrate.cs @@ -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()) - t.OnInfiltrate(target, self); + foreach (var t in target.Actor.TraitsImplementing()) + t.OnInfiltrate(target.Actor, self); if (self.HasTrait()) - 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()) + if (target.Actor.HasTrait()) Sound.PlayToPlayer(self.Owner, "bldginf1.aud"); return this; diff --git a/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs b/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs index 1c07b6d2de..3615d40485 100755 --- a/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs +++ b/OpenRA.Mods.RA/Activities/MoveAdjacentTo.cs @@ -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(); - - 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().FindBidiPath(ps1, ps2); - var ps2 = PathSearch.FromPoint( self.World, mobile.Info, self, mobile.toCell, target.CenterLocation.ToCPos(), true ); - var ret = self.World.WorldActor.Trait().FindBidiPath( ps1, ps2 ); - - return Util.SequenceActivities( mobile.MoveTo( () => ret ), this ); + return Util.SequenceActivities(mobile.MoveTo(() => ret), this); } } } diff --git a/OpenRA.Mods.RA/Activities/RepairBuilding.cs b/OpenRA.Mods.RA/Activities/RepairBuilding.cs index 54ffdcad37..8e5e4f4bc6 100644 --- a/OpenRA.Mods.RA/Activities/RepairBuilding.cs +++ b/OpenRA.Mods.RA/Activities/RepairBuilding.cs @@ -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(); + var health = target.Actor.Trait(); if (health.DamageState == DamageState.Undamaged) return NextActivity; - target.InflictDamage(self, -health.MaxHP, null); + target.Actor.InflictDamage(self, -health.MaxHP, null); self.Destroy(); return this; diff --git a/OpenRA.Mods.RA/C4Demolition.cs b/OpenRA.Mods.RA/C4Demolition.cs index 24c2e29483..cd46a527eb 100644 --- a/OpenRA.Mods.RA/C4Demolition.cs +++ b/OpenRA.Mods.RA/C4Demolition.cs @@ -52,11 +52,8 @@ namespace OpenRA.Mods.RA { self.SetTargetLine(Target.FromOrder(order), Color.Red); - var mobile = self.Trait(); 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))); } } diff --git a/OpenRA.Mods.RA/EngineerRepair.cs b/OpenRA.Mods.RA/EngineerRepair.cs index 92d4a055e8..1653a52605 100644 --- a/OpenRA.Mods.RA/EngineerRepair.cs +++ b/OpenRA.Mods.RA/EngineerRepair.cs @@ -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 { 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()) 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; } } diff --git a/OpenRA.Mods.RA/Infiltrates.cs b/OpenRA.Mods.RA/Infiltrates.cs index abd59a6b75..94aa3398a3 100644 --- a/OpenRA.Mods.RA/Infiltrates.cs +++ b/OpenRA.Mods.RA/Infiltrates.cs @@ -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))); } } diff --git a/OpenRA.Mods.RA/SupplyTruck.cs b/OpenRA.Mods.RA/SupplyTruck.cs index b14084f622..438740573c 100644 --- a/OpenRA.Mods.RA/SupplyTruck.cs +++ b/OpenRA.Mods.RA/SupplyTruck.cs @@ -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))); } }