From a1313b518d4fa6bba360554c92b6aa67674cbfe8 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 14 Apr 2013 07:13:29 +1200 Subject: [PATCH 1/9] Prevent multiple bridges spawning at the same location. --- OpenRA.Mods.RA/BridgeLayer.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/OpenRA.Mods.RA/BridgeLayer.cs b/OpenRA.Mods.RA/BridgeLayer.cs index 8e0df7e4dc..c3a187b921 100644 --- a/OpenRA.Mods.RA/BridgeLayer.cs +++ b/OpenRA.Mods.RA/BridgeLayer.cs @@ -85,16 +85,13 @@ namespace OpenRA.Mods.RA // For each subtile in the template for (byte ind = 0; ind < template.Size.X*template.Size.Y; ind++) { - // Is this tile actually included in the bridge template? - if (!template.Tiles.Keys.Contains(ind)) - continue; - // Where do we expect to find the subtile var x = ni + ind % template.Size.X; var y = nj + ind / template.Size.X; // This isn't the bridge you're looking for - if (!w.Map.IsInMap(x, y) || w.Map.MapTiles.Value[x, y].index != ind) + if (!w.Map.IsInMap(x, y) || w.Map.MapTiles.Value[x, y].type != tile || + w.Map.MapTiles.Value[x, y].index != ind) continue; subTiles.Add(new CPos(x, y), ind); From 8676562d4712868478f95846fbdf08f8fe509647 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 15 Apr 2013 01:41:12 +1200 Subject: [PATCH 2/9] Fix bogus pathability check. This fixes units on pathable bridge tiles from being killed on bridge death (they were failing the check against themselves). --- OpenRA.Mods.RA/Move/Mobile.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenRA.Mods.RA/Move/Mobile.cs b/OpenRA.Mods.RA/Move/Mobile.cs index 86b1757737..a3ab8fd27c 100755 --- a/OpenRA.Mods.RA/Move/Mobile.cs +++ b/OpenRA.Mods.RA/Move/Mobile.cs @@ -109,7 +109,8 @@ namespace OpenRA.Mods.RA.Move return true; var blockingActors = world.ActorMap.GetUnitsAt(cell) - .Where(x => x != ignoreActor) + // Don't fail if the unit is already in this cell + .Where(x => x != ignoreActor && x != self) // Neutral/enemy units are blockers. Allied units that are moving are not blockers. .Where(x => blockedByMovers || ((self.Owner.Stances[x.Owner] != Stance.Ally) || !IsMovingInMyDirection(self, x))) .ToList(); From e76c746b61cc239894e20ae050a099ea98f14910 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 15 Apr 2013 01:50:19 +1200 Subject: [PATCH 3/9] 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))); } } From e545865599a06123240104de0b4e2c997bf9b93a Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 15 Apr 2013 05:45:41 +1200 Subject: [PATCH 4/9] Add Selectable field to Selectable. There are a bunch of bogus assumptions about targetable actors being selectable. These aren't easily fixed, so this add a Selectable field that can be diabled for things we want to target, but not select. --- OpenRA.Game/Orders/UnitOrderGenerator.cs | 7 +++++-- OpenRA.Game/Traits/Selectable.cs | 16 ++++++++++++++-- .../Widgets/WorldInteractionControllerWidget.cs | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index f24de9b9c3..75d5fe3391 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -54,9 +54,12 @@ namespace OpenRA.Orders .OrderByDescending(a => a.SelectionPriority()) .FirstOrDefault(); - if (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any()) - if (underCursor != null && underCursor.HasTrait()) + if (underCursor != null && (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())) + { + var selectable = underCursor.TraitOrDefault(); + if (selectable != null && selectable.Info.Selectable) useSelect = true; + } var orders = world.Selection.Actors .Select(a => OrderForUnit(a, xy, mi, underCursor)) diff --git a/OpenRA.Game/Traits/Selectable.cs b/OpenRA.Game/Traits/Selectable.cs index 22562f6382..9fbaf02b4d 100644 --- a/OpenRA.Game/Traits/Selectable.cs +++ b/OpenRA.Game/Traits/Selectable.cs @@ -16,21 +16,30 @@ namespace OpenRA.Traits { public class SelectableInfo : ITraitInfo { + public readonly bool Selectable = true; public readonly int Priority = 10; public readonly int[] Bounds = null; [VoiceReference] public readonly string Voice = null; - public object Create(ActorInitializer init) { return new Selectable(init.self); } + public object Create(ActorInitializer init) { return new Selectable(init.self, this); } } public class Selectable : IPostRenderSelection { + public SelectableInfo Info; Actor self; - public Selectable(Actor self) { this.self = self; } + public Selectable(Actor self, SelectableInfo info) + { + this.self = self; + Info = info; + } public void RenderAfterWorld(WorldRenderer wr) { + if (!Info.Selectable) + return; + var bounds = self.Bounds.Value; var xy = new float2(bounds.Left, bounds.Top); @@ -44,6 +53,9 @@ namespace OpenRA.Traits public void DrawRollover(WorldRenderer wr, Actor self) { + if (!Info.Selectable) + return; + var bounds = self.Bounds.Value; var xy = new float2(bounds.Left, bounds.Top); diff --git a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs index b58609031b..4debabcfa1 100644 --- a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs @@ -184,7 +184,7 @@ namespace OpenRA.Widgets IEnumerable SelectActorsInBox(World world, PPos a, PPos b, Func cond) { return world.FindUnits(a, b) - .Where(x => x.HasTrait() && !world.FogObscures(x) && cond(x)) + .Where(x => x.HasTrait() && x.Trait().Info.Selectable && !world.FogObscures(x) && cond(x)) .GroupBy(x => x.GetSelectionPriority()) .OrderByDescending(g => g.Key) .Select(g => g.AsEnumerable()) From 4ca777597f72bcbb27000aaf629ad05e0534cfad Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 15 Apr 2013 22:31:09 +1200 Subject: [PATCH 5/9] Replace UnitTraitOrderTargeter with TargetTypeOrderTargeter. This also makes naval buildings untargetable for c4 and demo trucks, as they don't make much sense. --- OpenRA.Mods.RA/C4Demolition.cs | 5 +-- OpenRA.Mods.RA/Captures.cs | 32 ++++++++++++------- OpenRA.Mods.RA/DemoTruck.cs | 2 +- OpenRA.Mods.RA/EngineerRepair.cs | 2 +- OpenRA.Mods.RA/Infiltrates.cs | 12 ++++--- .../Orders/EnterBuildingOrderTargeter.cs | 19 +++++++---- OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs | 17 +++++++--- OpenRA.Mods.RA/Spy.cs | 2 +- OpenRA.Mods.RA/SupplyTruck.cs | 13 +++++--- mods/cnc-classic/rules/defaults.yaml | 2 +- mods/cnc/rules/defaults.yaml | 6 ++-- mods/d2k/rules/defaults.yaml | 3 +- mods/d2k/rules/structures.yaml | 2 +- mods/ra-classic/rules/defaults.yaml | 6 ++-- mods/ra/rules/civilian.yaml | 6 ++-- mods/ra/rules/defaults.yaml | 8 ++--- 16 files changed, 81 insertions(+), 56 deletions(-) diff --git a/OpenRA.Mods.RA/C4Demolition.cs b/OpenRA.Mods.RA/C4Demolition.cs index cd46a527eb..f200ef8bad 100644 --- a/OpenRA.Mods.RA/C4Demolition.cs +++ b/OpenRA.Mods.RA/C4Demolition.cs @@ -35,7 +35,7 @@ namespace OpenRA.Mods.RA public IEnumerable Orders { - get { yield return new UnitTraitOrderTargeter("C4", 6, "c4", true, false); } + get { yield return new TargetTypeOrderTargeter("C4", "C4", 6, "c4", true, false); } } public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued ) @@ -62,7 +62,4 @@ namespace OpenRA.Mods.RA return (order.OrderString == "C4") ? "Attack" : null; } } - - class C4DemolishableInfo : TraitInfo { } - class C4Demolishable { } } diff --git a/OpenRA.Mods.RA/Captures.cs b/OpenRA.Mods.RA/Captures.cs index 8f3700b4ef..08b1383e48 100644 --- a/OpenRA.Mods.RA/Captures.cs +++ b/OpenRA.Mods.RA/Captures.cs @@ -48,7 +48,7 @@ namespace OpenRA.Mods.RA public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued ) { - if( order.OrderID == "CaptureActor" ) + if (order.OrderID == "CaptureActor") return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; return null; @@ -64,7 +64,8 @@ namespace OpenRA.Mods.RA { if (order.OrderString == "CaptureActor") { - if (!CanCapture(order.TargetActor)) return; + if (!CanCapture(order.TargetActor)) + return; self.SetTargetLine(Target.FromOrder(order), Color.Red); @@ -76,17 +77,17 @@ namespace OpenRA.Mods.RA bool CanCapture(Actor target) { var c = target.TraitOrDefault(); - return c != null && ( !c.CaptureInProgress || c.Captor.Owner.Stances[self.Owner] != Stance.Ally ); + return c != null && (!c.CaptureInProgress || c.Captor.Owner.Stances[self.Owner] != Stance.Ally); } } - class CaptureOrderTargeter : UnitTraitOrderTargeter + class CaptureOrderTargeter : UnitOrderTargeter { readonly string[] captureTypes; readonly Func useEnterCursor; public CaptureOrderTargeter(string[] captureTypes, Func useEnterCursor) - : base( "CaptureActor", 6, "enter", true, true) + : base("CaptureActor", 6, "enter", true, true) { this.captureTypes = captureTypes; this.useEnterCursor = useEnterCursor; @@ -94,19 +95,26 @@ 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; - var ci = target.Info.Traits.Get(); - var playerRelationship = self.Owner.Stances[ target.Owner ]; + var ci = target.Info.Traits.GetOrDefault(); + if (ci == null) + return false; - if( playerRelationship == Stance.Ally && !ci.AllowAllies ) return false; - if( playerRelationship == Stance.Enemy && !ci.AllowEnemies ) return false; - if( playerRelationship == Stance.Neutral && !ci.AllowNeutral ) return false; + var playerRelationship = self.Owner.Stances[target.Owner]; + if (playerRelationship == Stance.Ally && !ci.AllowAllies) + return false; + + if (playerRelationship == Stance.Enemy && !ci.AllowEnemies) + return false; + + if (playerRelationship == Stance.Neutral && !ci.AllowNeutral) + return false; IsQueued = forceQueued; var Info = self.Info.Traits.Get(); - if (captureTypes.Contains(ci.Type)) { cursor = (Info.WastedAfterwards) ? (useEnterCursor(target) ? "enter" : "enter-blocked") : "attack"; diff --git a/OpenRA.Mods.RA/DemoTruck.cs b/OpenRA.Mods.RA/DemoTruck.cs index 9f20a4d330..895a38283c 100644 --- a/OpenRA.Mods.RA/DemoTruck.cs +++ b/OpenRA.Mods.RA/DemoTruck.cs @@ -34,7 +34,7 @@ namespace OpenRA.Mods.RA { get { - yield return new UnitTraitOrderTargeter("DemoAttack", 5, "attack", true, false) { ForceAttack = false }; + yield return new TargetTypeOrderTargeter("DemoTruck", "DemoAttack", 5, "attack", true, false) { ForceAttack = false }; yield return new DeployOrderTargeter("DemoDeploy", 5); } } diff --git a/OpenRA.Mods.RA/EngineerRepair.cs b/OpenRA.Mods.RA/EngineerRepair.cs index 1653a52605..d8cfe5a793 100644 --- a/OpenRA.Mods.RA/EngineerRepair.cs +++ b/OpenRA.Mods.RA/EngineerRepair.cs @@ -52,7 +52,7 @@ namespace OpenRA.Mods.RA } } - class EngineerRepairOrderTargeter : UnitTraitOrderTargeter + class EngineerRepairOrderTargeter : UnitOrderTargeter { public EngineerRepairOrderTargeter() : base("EngineerRepair", 6, "goldwrench", false, true) { } diff --git a/OpenRA.Mods.RA/Infiltrates.cs b/OpenRA.Mods.RA/Infiltrates.cs index 94aa3398a3..1f684c834b 100644 --- a/OpenRA.Mods.RA/Infiltrates.cs +++ b/OpenRA.Mods.RA/Infiltrates.cs @@ -88,13 +88,14 @@ namespace OpenRA.Mods.RA return false; } - class InfiltratorOrderTargeter : UnitTraitOrderTargeter + class InfiltratorOrderTargeter : UnitOrderTargeter { readonly Func useEnterCursor; - public InfiltratorOrderTargeter(Func useEnterCursor) : base("Infiltrate", 7, "enter", true, false) + public InfiltratorOrderTargeter(Func useEnterCursor) + : base("Infiltrate", 7, "enter", true, false) { - ForceAttack=false; + ForceAttack = false; this.useEnterCursor = useEnterCursor; } @@ -102,7 +103,10 @@ namespace OpenRA.Mods.RA { if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor)) return false; - + + if (!target.HasTrait()) + return false; + if (!useEnterCursor(target)) cursor = "enter-blocked"; diff --git a/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs b/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs index 076ae61f19..8f314e5381 100755 --- a/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs @@ -12,14 +12,14 @@ using System; namespace OpenRA.Mods.RA.Orders { - public class EnterOrderTargeter : UnitTraitOrderTargeter + public class EnterOrderTargeter : UnitOrderTargeter { readonly Func canTarget; readonly Func useEnterCursor; - public EnterOrderTargeter( string order, int priority, bool targetEnemy, bool targetAlly, - Func canTarget, Func useEnterCursor ) - : base( order, priority, "enter", targetEnemy, targetAlly ) + public EnterOrderTargeter(string order, int priority, bool targetEnemy, bool targetAlly, + Func canTarget, Func useEnterCursor) + : base (order, priority, "enter", targetEnemy, targetAlly) { this.canTarget = canTarget; this.useEnterCursor = useEnterCursor; @@ -27,8 +27,15 @@ namespace OpenRA.Mods.RA.Orders 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( !canTarget( target ) ) return false; + if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor)) + return false; + + if (!target.HasTrait()) + return false; + + if (!canTarget(target)) + return false; + cursor = useEnterCursor(target) ? "enter" : "enter-blocked"; IsQueued = forceQueued; return true; diff --git a/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs b/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs index 5acf4d9b91..f0e4d101d4 100755 --- a/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; +using System.Linq; using OpenRA.Traits; namespace OpenRA.Mods.RA.Orders @@ -57,17 +58,23 @@ namespace OpenRA.Mods.RA.Orders public virtual bool IsQueued { get; protected set; } } - public class UnitTraitOrderTargeter : UnitOrderTargeter + public class TargetTypeOrderTargeter : UnitOrderTargeter { - public UnitTraitOrderTargeter( string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits ) - : base( order, priority, cursor, targetEnemyUnits, targetAllyUnits ) + string targetType; + + public TargetTypeOrderTargeter(string targetType, string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits) + : base(order, priority, cursor, targetEnemyUnits, targetAllyUnits) { + this.targetType = targetType; } 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( !target.HasTrait() ) return false; + if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor)) + return false; + + if (!target.TraitsImplementing().Any(t => t.TargetTypes.Contains(targetType))) + return false; IsQueued = forceQueued; diff --git a/OpenRA.Mods.RA/Spy.cs b/OpenRA.Mods.RA/Spy.cs index 29d05d483d..083769cbac 100644 --- a/OpenRA.Mods.RA/Spy.cs +++ b/OpenRA.Mods.RA/Spy.cs @@ -84,7 +84,7 @@ namespace OpenRA.Mods.RA { get { - yield return new UnitTraitOrderTargeter("Disguise", 7, "ability", true, true) { ForceAttack=false }; + yield return new TargetTypeOrderTargeter("Disguise", "Disguise", 7, "ability", true, true) { ForceAttack=false }; } } diff --git a/OpenRA.Mods.RA/SupplyTruck.cs b/OpenRA.Mods.RA/SupplyTruck.cs index 438740573c..2253bc3141 100644 --- a/OpenRA.Mods.RA/SupplyTruck.cs +++ b/OpenRA.Mods.RA/SupplyTruck.cs @@ -63,7 +63,7 @@ namespace OpenRA.Mods.RA } } - class SupplyTruckOrderTargeter : UnitTraitOrderTargeter + class SupplyTruckOrderTargeter : UnitOrderTargeter { public SupplyTruckOrderTargeter() : base("DeliverSupplies", 5, "enter", false, true) @@ -72,9 +72,14 @@ 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 (target.AppearsHostileTo(self)) return false; - if (!target.HasTrait()) return false; + if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor)) + return false; + + if (target.AppearsHostileTo(self)) + return false; + + if (!target.HasTrait()) + return false; IsQueued = forceQueued; return true; diff --git a/mods/cnc-classic/rules/defaults.yaml b/mods/cnc-classic/rules/defaults.yaml index c3437f55f7..972a72f4ee 100644 --- a/mods/cnc-classic/rules/defaults.yaml +++ b/mods/cnc-classic/rules/defaults.yaml @@ -208,7 +208,7 @@ Selectable: Priority: 3 TargetableBuilding: - TargetTypes: Ground + TargetTypes: Ground, C4 Armor: Type: Wood RepairableBuilding: diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 7615dfcc2c..04c024e67b 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -225,7 +225,7 @@ Selectable: Priority: 3 TargetableBuilding: - TargetTypes: Ground + TargetTypes: Ground, C4 Armor: Type: Wood RepairableBuilding: @@ -267,7 +267,6 @@ Sellable: Capturable: CapturableBar: - C4Demolishable: DebugMuzzlePositions: ^CivBuilding: @@ -339,7 +338,7 @@ Adjacent: 7 TerrainTypes: Clear,Road TargetableBuilding: - TargetTypes: Ground + TargetTypes: Ground, C4 Wall: CrushClasses: wall CrushSound: sandbag2.aud @@ -356,7 +355,6 @@ RelativeToTopLeft: yes AutoTargetIgnore: Sellable: - C4Demolishable: ^Tree: Tooltip: diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 0c88cd9aea..833cf7c74d 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -215,7 +215,7 @@ Selectable: Priority: 2 TargetableBuilding: - TargetTypes: Ground + TargetTypes: Ground, C4 Building: Dimensions: 1,1 Footprint: x @@ -263,6 +263,5 @@ Types:Building Sellable: GivesBounty: - C4Demolishable: DebugMuzzlePositions: Bib: diff --git a/mods/d2k/rules/structures.yaml b/mods/d2k/rules/structures.yaml index fce4faf0c0..b565293d9b 100644 --- a/mods/d2k/rules/structures.yaml +++ b/mods/d2k/rules/structures.yaml @@ -433,7 +433,7 @@ WALL: #Selectable: # Priority: 1 TargetableBuilding: - TargetTypes: Ground + TargetTypes: Ground, C4 RenderBuildingWall: HasMakeAnimation: false #GivesExperience: diff --git a/mods/ra-classic/rules/defaults.yaml b/mods/ra-classic/rules/defaults.yaml index 35bfbca04e..ecf1617fcd 100644 --- a/mods/ra-classic/rules/defaults.yaml +++ b/mods/ra-classic/rules/defaults.yaml @@ -83,7 +83,7 @@ Selectable: Voice: GenericVoice TargetableUnit: - TargetTypes: Ground + TargetTypes: Ground, Disguise RenderInfantry: AutoTarget: AttackMove: @@ -164,7 +164,7 @@ Selectable: Priority: 3 TargetableBuilding: - TargetTypes: Ground + TargetTypes: Ground, C4, DemoTruck Building: Dimensions: 1,1 Footprint: x @@ -210,7 +210,7 @@ Selectable: Priority: 1 TargetableBuilding: - TargetTypes: Ground + TargetTypes: Ground, C4, DemoTruck RenderBuildingWall: HasMakeAnimation: false Palette: terrain diff --git a/mods/ra/rules/civilian.yaml b/mods/ra/rules/civilian.yaml index eadc86ec7e..34f3d626a3 100644 --- a/mods/ra/rules/civilian.yaml +++ b/mods/ra/rules/civilian.yaml @@ -246,7 +246,8 @@ BARL: AutoTargetIgnore: Armor: Type: Light - -C4Demolishable: + TargetableBuilding: + TargetTypes: Ground, DemoTruck BRL3: Inherits: ^TechBuilding @@ -261,7 +262,8 @@ BRL3: AutoTargetIgnore: Armor: Type: Light - -C4Demolishable: + TargetableBuilding: + TargetTypes: Ground, DemoTruck MISS: Inherits: ^TechBuilding diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index b06075f5c2..954c4a685e 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -96,7 +96,7 @@ Selectable: Voice: GenericVoice TargetableUnit: - TargetTypes: Ground + TargetTypes: Ground, Disguise RenderInfantry: AutoTarget: DebugRetiliateAgainstAggressor: @@ -196,7 +196,7 @@ Selectable: Priority: 3 TargetableBuilding: - TargetTypes: Ground + TargetTypes: Ground, C4, DemoTruck Building: Dimensions: 1,1 Footprint: x @@ -227,7 +227,6 @@ AcceptsSupplies: GivesBounty: UpdatesPlayerStatistics: - C4Demolishable: DebugMuzzlePositions: ^Wall: @@ -249,7 +248,7 @@ Selectable: Priority: 1 TargetableBuilding: - TargetTypes: Ground + TargetTypes: Ground, C4, DemoTruck RenderBuildingWall: HasMakeAnimation: false Palette: terrain @@ -262,7 +261,6 @@ Types:Wall Sellable: UpdatesPlayerStatistics: - C4Demolishable: ^TechBuilding: Inherits: ^Building From d3959d21caa781503d02c0bac7e55cf23a73aae5 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 15 Apr 2013 00:10:07 +1200 Subject: [PATCH 6/9] Add a ParentActorInit for FreeActor spawns. --- OpenRA.Mods.RA/FreeActor.cs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/OpenRA.Mods.RA/FreeActor.cs b/OpenRA.Mods.RA/FreeActor.cs index cab41feb88..fb5199e333 100644 --- a/OpenRA.Mods.RA/FreeActor.cs +++ b/OpenRA.Mods.RA/FreeActor.cs @@ -34,21 +34,22 @@ namespace OpenRA.Mods.RA { public FreeActor(ActorInitializer init, FreeActorInfo info) { - if (init.Contains() && !init.Get().value) return; + if (init.Contains() && !init.Get().value) + return; - init.self.World.AddFrameEndTask( - w => + init.self.World.AddFrameEndTask(w => + { + var a = w.CreateActor(info.Actor, new TypeDictionary { - var a = w.CreateActor(info.Actor, new TypeDictionary - { - new LocationInit( init.self.Location + (CVec)info.SpawnOffset ), - new OwnerInit( init.self.Owner ), - new FacingInit( info.Facing ), - }); - - if (info.InitialActivity != null) - a.QueueActivity(Game.CreateObject(info.InitialActivity)); + new ParentActorInit(init.self), + new LocationInit(init.self.Location + (CVec)info.SpawnOffset), + new OwnerInit(init.self.Owner), + new FacingInit(info.Facing), }); + + if (info.InitialActivity != null) + a.QueueActivity(Game.CreateObject(info.InitialActivity)); + }); } } @@ -60,4 +61,11 @@ namespace OpenRA.Mods.RA public FreeActorInit(bool init) { value = init; } public bool Value(World world) { return value; } } + + public class ParentActorInit : IActorInit + { + public readonly Actor value; + public ParentActorInit(Actor parent) { value = parent; } + public Actor Value(World world) { return value; } + } } From 115da5c770daf7c0513a4ec690c4d9659173bdc2 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 15 Apr 2013 00:08:40 +1200 Subject: [PATCH 7/9] Make bridges repairable. --- OpenRA.Game/Traits/Health.cs | 29 +++++++ OpenRA.Mods.RA/Bridge.cs | 145 +++++++++++++++++++++++++++-------- 2 files changed, 143 insertions(+), 31 deletions(-) diff --git a/OpenRA.Game/Traits/Health.cs b/OpenRA.Game/Traits/Health.cs index 15cc0e12bb..f62c979b71 100755 --- a/OpenRA.Game/Traits/Health.cs +++ b/OpenRA.Game/Traits/Health.cs @@ -69,6 +69,35 @@ namespace OpenRA.Traits } } + public void Resurrect(Actor self, Actor repairer) + { + if (!IsDead) + return; + + hp = MaxHP; + + var ai = new AttackInfo + { + Attacker = repairer, + Damage = -MaxHP, + DamageState = this.DamageState, + PreviousDamageState = DamageState.Dead, + Warhead = null, + }; + + foreach (var nd in self.TraitsImplementing() + .Concat(self.Owner.PlayerActor.TraitsImplementing())) + nd.Damaged(self, ai); + + foreach (var nd in self.TraitsImplementing()) + nd.DamageStateChanged(self, ai); + + if (repairer != null && repairer.IsInWorld && !repairer.IsDead()) + foreach (var nd in repairer.TraitsImplementing() + .Concat(repairer.Owner.PlayerActor.TraitsImplementing())) + nd.AppliedDamage(repairer, self, ai); + } + public void InflictDamage(Actor self, Actor attacker, int damage, WarheadInfo warhead, bool ignoreModifiers) { if (IsDead) return; /* overkill! don't count extra hits as more kills! */ diff --git a/OpenRA.Mods.RA/Bridge.cs b/OpenRA.Mods.RA/Bridge.cs index b98df7974f..495e006ce6 100644 --- a/OpenRA.Mods.RA/Bridge.cs +++ b/OpenRA.Mods.RA/Bridge.cs @@ -8,11 +8,14 @@ */ #endregion +using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using OpenRA.Effects; using OpenRA.FileFormats; using OpenRA.Graphics; +using OpenRA.Mods.RA.Move; using OpenRA.Traits; namespace OpenRA.Mods.RA @@ -21,6 +24,8 @@ namespace OpenRA.Mods.RA { public readonly bool Long = false; + [Desc("Delay (in ticks) between repairing adjacent spans in long bridges")] + public readonly int RepairPropagationDelay = 20; public readonly ushort Template = 0; public readonly ushort DamagedTemplate = 0; @@ -149,11 +154,6 @@ namespace OpenRA.Mods.RA yield return new Renderable(t.Value, t.Key.ToPPos().ToFloat2(), terrainPalette, Game.CellSize * t.Key.Y); } - bool IsIntact(Bridge b) - { - return b != null && !b.self.IsDead(); - } - void KillUnitsOnBridge() { foreach (var c in TileSprites[currentTemplate].Keys) @@ -162,35 +162,55 @@ namespace OpenRA.Mods.RA a.Kill(self); } - bool dead = false; - void UpdateState() + bool NeighbourIsDeadShore(Bridge neighbour) { - // If this is a long bridge next to a destroyed shore piece, we need die to give clean edges to the break - if (Info.Long && Health.DamageState != DamageState.Dead && - ((southNeighbour != null && Info.ShorePieces.Contains(southNeighbour.Type) && !IsIntact(southNeighbour)) || - (northNeighbour != null && Info.ShorePieces.Contains(northNeighbour.Type) && !IsIntact(northNeighbour)))) - { - self.Kill(self); // this changes the damagestate - } - var oldTemplate = currentTemplate; - var ds = Health.DamageState; - currentTemplate = (ds == DamageState.Dead && Info.DestroyedTemplate > 0) ? Info.DestroyedTemplate : - (ds >= DamageState.Heavy && Info.DamagedTemplate > 0) ? Info.DamagedTemplate : Info.Template; + return neighbour != null && Info.ShorePieces.Contains(neighbour.Type) && neighbour.Health.IsDead; + } - if (Info.Long && ds == DamageState.Dead) + bool LongBridgeSegmentIsDead() + { + // The long bridge artwork requires a hack to display correctly + // if the adjacent shore piece is dead + if (!Info.Long) + return Health.IsDead; + + if (NeighbourIsDeadShore(northNeighbour)) + return true; + + if (NeighbourIsDeadShore(southNeighbour)) + return true; + + return Health.IsDead; + } + + ushort ChooseTemplate() + { + if (Info.Long && LongBridgeSegmentIsDead()) { // Long bridges have custom art for multiple segments being destroyed - bool waterToSouth = !IsIntact(southNeighbour); - bool waterToNorth = !IsIntact(northNeighbour); + var northIsDead = northNeighbour != null && northNeighbour.LongBridgeSegmentIsDead(); + var southIsDead = southNeighbour != null && southNeighbour.LongBridgeSegmentIsDead(); + if (northIsDead && southIsDead) + return Info.DestroyedPlusBothTemplate; + if (northIsDead) + return Info.DestroyedPlusNorthTemplate; + if (southIsDead) + return Info.DestroyedPlusSouthTemplate; - if (waterToSouth && waterToNorth) - currentTemplate = Info.DestroyedPlusBothTemplate; - else if (waterToNorth) - currentTemplate = Info.DestroyedPlusNorthTemplate; - else if (waterToSouth) - currentTemplate = Info.DestroyedPlusSouthTemplate; + return Info.DestroyedTemplate; } + var ds = Health.DamageState; + return (ds == DamageState.Dead && Info.DestroyedTemplate > 0) ? Info.DestroyedTemplate : + (ds >= DamageState.Heavy && Info.DamagedTemplate > 0) ? Info.DamagedTemplate : Info.Template; + } + + bool killedUnits = false; + void UpdateState() + { + var oldTemplate = currentTemplate; + + currentTemplate = ChooseTemplate(); if (currentTemplate == oldTemplate) return; @@ -198,18 +218,81 @@ namespace OpenRA.Mods.RA foreach (var c in TileSprites[currentTemplate].Keys) self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c); - if (ds == DamageState.Dead && !dead) + if (LongBridgeSegmentIsDead() && !killedUnits) { - dead = true; + killedUnits = true; KillUnitsOnBridge(); } } + public void Repair(Actor repairer, bool continueNorth, bool continueSouth) + { + // Repair self + var initialDamage = Health.DamageState; + self.World.AddFrameEndTask(w => + { + if (Health.IsDead) + { + Health.Resurrect(self, repairer); + killedUnits = false; + KillUnitsOnBridge(); + } + else + Health.InflictDamage(self, repairer, -Health.MaxHP, null, true); + }); + + // Repair adjacent spans (long bridges) + if (continueNorth && northNeighbour != null) + { + var delay = initialDamage == DamageState.Undamaged || NeighbourIsDeadShore(northNeighbour) ? + 0 : Info.RepairPropagationDelay; + + self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () => + northNeighbour.Repair(repairer, true, false)))); + } + + if (continueSouth && southNeighbour != null) + { + var delay = initialDamage == DamageState.Undamaged || NeighbourIsDeadShore(southNeighbour) ? + 0 : Info.RepairPropagationDelay; + + self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () => + southNeighbour.Repair(repairer, false, true)))); + } + } + public void DamageStateChanged(Actor self, AttackInfo e) { UpdateState(); - if (northNeighbour != null) northNeighbour.UpdateState(); - if (southNeighbour != null) southNeighbour.UpdateState(); + if (northNeighbour != null) + northNeighbour.UpdateState(); + if (southNeighbour != null) + southNeighbour.UpdateState(); + + // Need to update the neighbours neighbour to correctly + // display the broken shore hack + if (Info.ShorePieces.Contains(Type)) + { + if (northNeighbour != null && northNeighbour.northNeighbour != null) + northNeighbour.northNeighbour.UpdateState(); + if (southNeighbour != null && southNeighbour.southNeighbour != null) + southNeighbour.southNeighbour.UpdateState(); + } + } + + public DamageState AggregateDamageState() + { + // Find the worst span damage in the entire bridge + var br = this; + while (br.northNeighbour != null) + br = br.northNeighbour; + + var damage = Health.DamageState; + for (var b = br; b != null; b = b.southNeighbour) + if (b.Health.DamageState > damage) + damage = b.Health.DamageState; + + return damage; } } } From 08cf941fef341c5c43c6ab004f1f274a30c6629f Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 15 Apr 2013 00:11:36 +1200 Subject: [PATCH 8/9] Add BridgeHut and repair activities. --- OpenRA.Mods.RA/Activities/RepairBridge.cs | 37 +++++++++ OpenRA.Mods.RA/BridgeHut.cs | 41 ++++++++++ OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 3 + OpenRA.Mods.RA/RepairsBridges.cs | 96 +++++++++++++++++++++++ 4 files changed, 177 insertions(+) create mode 100644 OpenRA.Mods.RA/Activities/RepairBridge.cs create mode 100644 OpenRA.Mods.RA/BridgeHut.cs create mode 100644 OpenRA.Mods.RA/RepairsBridges.cs diff --git a/OpenRA.Mods.RA/Activities/RepairBridge.cs b/OpenRA.Mods.RA/Activities/RepairBridge.cs new file mode 100644 index 0000000000..90377a47e1 --- /dev/null +++ b/OpenRA.Mods.RA/Activities/RepairBridge.cs @@ -0,0 +1,37 @@ +#region Copyright & License Information +/* + * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Activities +{ + class RepairBridge : Activity + { + Target target; + + public RepairBridge(Actor target) { this.target = Target.FromActor(target); } + + public override Activity Tick(Actor self) + { + if (IsCanceled || !target.IsValid) + return NextActivity; + + var hut = target.Actor.Trait(); + if (hut.BridgeDamageState == DamageState.Undamaged) + return NextActivity; + + hut.Repair(self); + self.Destroy(); + + return this; + } + } +} diff --git a/OpenRA.Mods.RA/BridgeHut.cs b/OpenRA.Mods.RA/BridgeHut.cs new file mode 100644 index 0000000000..b55590bf6e --- /dev/null +++ b/OpenRA.Mods.RA/BridgeHut.cs @@ -0,0 +1,41 @@ +#region Copyright & License Information +/* + * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class BridgeHutInfo : ITraitInfo + { + public object Create(ActorInitializer init) { return new BridgeHut(init); } + } + + class BridgeHut + { + public Bridge bridge; + + public BridgeHut(ActorInitializer init) + { + bridge = init.Get().value.Trait(); + } + + public void Repair(Actor repairer) + { + bridge.Repair(repairer, true, true); + } + + public DamageState BridgeDamageState { get { return bridge.AggregateDamageState(); } } + } +} diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index b9a2febadd..b4d529fd2d 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -423,6 +423,9 @@ + + + diff --git a/OpenRA.Mods.RA/RepairsBridges.cs b/OpenRA.Mods.RA/RepairsBridges.cs new file mode 100644 index 0000000000..08fa795f89 --- /dev/null +++ b/OpenRA.Mods.RA/RepairsBridges.cs @@ -0,0 +1,96 @@ +#region Copyright & License Information +/* + * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Buildings; +using OpenRA.Mods.RA.Orders; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class RepairsBridgesInfo : TraitInfo {} + + class RepairsBridges : IIssueOrder, IResolveOrder, IOrderVoice + { + public IEnumerable Orders + { + get { yield return new RepairBridgeOrderTargeter(); } + } + + public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) + { + if (order.OrderID == "RepairBridge") + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + + return null; + } + + public string VoicePhraseForOrder(Actor self, Order order) + { + if (order.OrderString != "RepairBridge") + return null; + + var bridge = order.TargetActor.TraitOrDefault(); + if (bridge == null) + return null; + + return bridge.BridgeDamageState > DamageState.Undamaged ? "Attack" : null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "RepairBridge") + { + var bridge = order.TargetActor.TraitOrDefault(); + if (bridge == null) + return; + + if (bridge.BridgeDamageState == DamageState.Undamaged) + return; + + self.SetTargetLine(Target.FromOrder(order), Color.Yellow); + + self.CancelActivity(); + self.QueueActivity(new Enter(order.TargetActor, new RepairBridge(order.TargetActor))); + } + } + + class RepairBridgeOrderTargeter : UnitOrderTargeter + { + public RepairBridgeOrderTargeter() + : base("RepairBridge", 6, "goldwrench", true, 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; + + var bridge = target.TraitOrDefault(); + if (bridge == null) + return false; + + // Require force attack to heal partially damaged bridges to avoid unnecessary cursor noise + var damage = bridge.BridgeDamageState; + if (!forceAttack && damage != DamageState.Dead) + return false; + + IsQueued = forceQueued; + + // Can't repair an undamaged bridge + if (damage == DamageState.Undamaged) + cursor = "goldwrench-blocked"; + + return true; + } + } + } +} From 921c7b4fb1c8c39b4fc74af206f6f39ae4ec0b49 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 15 Apr 2013 05:47:18 +1200 Subject: [PATCH 9/9] Add invisible repair zones to bridges. --- mods/cnc-classic/rules/civilian.yaml | 35 +++++++++++++++ mods/cnc-classic/rules/defaults.yaml | 3 -- mods/cnc-classic/rules/infantry.yaml | 1 + mods/cnc/rules/civilian.yaml | 35 +++++++++++++++ mods/cnc/rules/defaults.yaml | 5 +-- mods/cnc/rules/infantry.yaml | 1 + mods/ra-classic/rules/civilian.yaml | 31 ++++++++++++- mods/ra-classic/rules/infantry.yaml | 1 + mods/ra/rules/civilian.yaml | 67 ++++++++++++++++++++++++++++ mods/ra/rules/infantry.yaml | 1 + 10 files changed, 172 insertions(+), 8 deletions(-) diff --git a/mods/cnc-classic/rules/civilian.yaml b/mods/cnc-classic/rules/civilian.yaml index 5eb8bb9d40..6f16f6ac76 100644 --- a/mods/cnc-classic/rules/civilian.yaml +++ b/mods/cnc-classic/rules/civilian.yaml @@ -145,6 +145,12 @@ BRIDGE1: Building: Footprint: ____ ____ ____ ____ Dimensions: 4,4 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 2,0 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 0,2 BRIDGE2: Inherits: ^Bridge @@ -154,6 +160,12 @@ BRIDGE2: Building: Footprint: _____ _____ _____ _____ _____ Dimensions: 5,5 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 0,0 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 2,2 BRIDGE3: Inherits: ^Bridge @@ -163,6 +175,12 @@ BRIDGE3: Building: Footprint: ______ ______ ______ ______ ______ Dimensions: 6,5 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 3,0 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 1,2 BRIDGE4: Inherits: ^Bridge @@ -172,6 +190,23 @@ BRIDGE4: Building: Footprint: ______ ______ ______ ______ Dimensions: 6,4 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 1,0 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 3,2 + +BRIDGEHUT: + Building: + Footprint: __ __ + Dimensions: 2,2 + Selectable: + Selectable: false + Bounds: 48,48 + BridgeHut: + TargetableBuilding: + TargetTypes: BridgeHut C1: Inherits: ^CivInfantry diff --git a/mods/cnc-classic/rules/defaults.yaml b/mods/cnc-classic/rules/defaults.yaml index 972a72f4ee..0418b2f474 100644 --- a/mods/cnc-classic/rules/defaults.yaml +++ b/mods/cnc-classic/rules/defaults.yaml @@ -399,6 +399,3 @@ SoundOnDamageTransition: DamagedSound: xplos.aud DestroyedSound: xplobig4.aud - Building: - Footprint: ______ ______ ______ ______ - Dimensions: 6,4 diff --git a/mods/cnc-classic/rules/infantry.yaml b/mods/cnc-classic/rules/infantry.yaml index 254ef72d58..fa99a1afae 100644 --- a/mods/cnc-classic/rules/infantry.yaml +++ b/mods/cnc-classic/rules/infantry.yaml @@ -182,6 +182,7 @@ E6: Passenger: PipType: Yellow EngineerRepair: + RepairsBridges: Captures: CaptureTypes: building, husk -AutoTarget: diff --git a/mods/cnc/rules/civilian.yaml b/mods/cnc/rules/civilian.yaml index a9e052898b..17aa688b0c 100644 --- a/mods/cnc/rules/civilian.yaml +++ b/mods/cnc/rules/civilian.yaml @@ -302,6 +302,12 @@ BRIDGE1: Building: Footprint: ____ ____ ____ ____ Dimensions: 4,4 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 2,0 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 0,2 BRIDGE2: Inherits: ^Bridge @@ -311,6 +317,12 @@ BRIDGE2: Building: Footprint: _____ _____ _____ _____ _____ Dimensions: 5,5 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 0,0 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 2,2 BRIDGE3: Inherits: ^Bridge @@ -320,6 +332,12 @@ BRIDGE3: Building: Footprint: ______ ______ ______ ______ ______ Dimensions: 6,5 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 3,0 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 1,2 BRIDGE4: Inherits: ^Bridge @@ -329,6 +347,23 @@ BRIDGE4: Building: Footprint: ______ ______ ______ ______ Dimensions: 6,4 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 1,0 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 3,2 + +BRIDGEHUT: + Building: + Footprint: __ __ + Dimensions: 2,2 + Selectable: + Selectable: false + Bounds: 48,48 + BridgeHut: + TargetableBuilding: + TargetTypes: BridgeHut C1: Inherits: ^CivInfantry diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 04c024e67b..0e4b262346 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -417,10 +417,7 @@ TargetTypes: Ground, Water BelowUnits: Health: -# HP: 500 + HP: 500 SoundOnDamageTransition: DamagedSound: xplos.aud DestroyedSound: xplobig4.aud - Building: - Footprint: ______ ______ ______ ______ - Dimensions: 6,4 diff --git a/mods/cnc/rules/infantry.yaml b/mods/cnc/rules/infantry.yaml index fdda163d5f..d25fb54bd9 100644 --- a/mods/cnc/rules/infantry.yaml +++ b/mods/cnc/rules/infantry.yaml @@ -166,6 +166,7 @@ E6: Passenger: PipType: Yellow EngineerRepair: + RepairsBridges: Captures: CaptureTypes: building, husk -AutoTarget: diff --git a/mods/ra-classic/rules/civilian.yaml b/mods/ra-classic/rules/civilian.yaml index 5f6e996055..48caa6cc2e 100644 --- a/mods/ra-classic/rules/civilian.yaml +++ b/mods/ra-classic/rules/civilian.yaml @@ -232,6 +232,9 @@ BR1: DamagedTemplate: 236 DestroyedTemplate: 237 SouthOffset: 0,2 + FreeActor: + Actor: bridgehut + SpawnOffset: 2,0 BR2: Inherits: ^Bridge @@ -240,6 +243,9 @@ BR2: DamagedTemplate: 239 DestroyedTemplate: 240 NorthOffset: 3,0 + FreeActor: + Actor: bridgehut + SpawnOffset: 1,1 BR3: Inherits: ^Bridge @@ -264,6 +270,12 @@ BRIDGE1: Building: Footprint: _____ _____ _____ Dimensions: 5,3 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 2,-1 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 0,1 BRIDGE2: Inherits: ^Bridge @@ -273,4 +285,21 @@ BRIDGE2: DestroyedTemplate: 134 Building: Footprint: _____ _____ - Dimensions: 5,2 \ No newline at end of file + Dimensions: 5,2 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 0,-1 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 2,1 + +BRIDGEHUT: + Building: + Footprint: __ __ + Dimensions: 2,2 + Selectable: + Selectable: false + Bounds: 48,48 + BridgeHut: + TargetableBuilding: + TargetTypes: BridgeHut diff --git a/mods/ra-classic/rules/infantry.yaml b/mods/ra-classic/rules/infantry.yaml index d4c80ef26d..cfe1f3d2a6 100644 --- a/mods/ra-classic/rules/infantry.yaml +++ b/mods/ra-classic/rules/infantry.yaml @@ -162,6 +162,7 @@ E6: Passenger: PipType: Yellow EngineerRepair: + RepairsBridges: Captures: Sabotage: yes TakeCover: diff --git a/mods/ra/rules/civilian.yaml b/mods/ra/rules/civilian.yaml index 34f3d626a3..11499c8093 100644 --- a/mods/ra/rules/civilian.yaml +++ b/mods/ra/rules/civilian.yaml @@ -322,6 +322,9 @@ BR1: DamagedTemplate: 236 DestroyedTemplate: 237 SouthOffset: 0,2 + FreeActor: + Actor: bridgehut + SpawnOffset: 2,0 BR2: Inherits: ^Bridge @@ -330,6 +333,9 @@ BR2: DamagedTemplate: 239 DestroyedTemplate: 240 NorthOffset: 3,0 + FreeActor: + Actor: bridgehut + SpawnOffset: 1,1 BR3: Inherits: ^Bridge @@ -354,6 +360,12 @@ BRIDGE1: Building: Footprint: _____ _____ _____ Dimensions: 5,3 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 2,-1 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 0,1 BRIDGE2: Inherits: ^Bridge @@ -364,6 +376,12 @@ BRIDGE2: Building: Footprint: _____ _____ Dimensions: 5,2 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 0,-1 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 2,1 SBRIDGE1: Inherits: ^SVBridge @@ -374,6 +392,13 @@ SBRIDGE1: Building: Footprint: ___ ___ Dimensions: 3,2 + FreeActor@north: + Actor: bridgehut.small + SpawnOffset: 1,0 + FreeActor@south: + Actor: bridgehut.small + SpawnOffset: 1,1 + SBRIDGE2: Inherits: ^SHBridge Bridge: @@ -383,6 +408,13 @@ SBRIDGE2: Building: Footprint: __ __ __ Dimensions: 2,3 + FreeActor@west: + Actor: bridgehut.small + SpawnOffset: 0,1 + FreeActor@east: + Actor: bridgehut.small + SpawnOffset: 1,1 + SBRIDGE3: Inherits: ^STDBridge Bridge: @@ -392,6 +424,13 @@ SBRIDGE3: Building: Footprint: ____ ____ Dimensions: 4,2 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 2,-1 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 0,1 + SBRIDGE4: Inherits: ^STDBridge Bridge: @@ -401,6 +440,34 @@ SBRIDGE4: Building: Footprint: ____ ____ Dimensions: 4,2 + FreeActor@north: + Actor: bridgehut + SpawnOffset: 0,-1 + FreeActor@south: + Actor: bridgehut + SpawnOffset: 2,1 + +BRIDGEHUT: + Building: + Footprint: __ __ + Dimensions: 2,2 + Selectable: + Selectable: false + Bounds: 48,48 + BridgeHut: + TargetableBuilding: + TargetTypes: BridgeHut + +BRIDGEHUT.small: + Building: + Footprint: _ + Dimensions: 1,1 + Selectable: + Selectable: false + Bounds: 24,24 + BridgeHut: + TargetableBuilding: + TargetTypes: BridgeHut #Desert Terrain Expansion V20: diff --git a/mods/ra/rules/infantry.yaml b/mods/ra/rules/infantry.yaml index 0c0da76fb2..b61d8623bf 100644 --- a/mods/ra/rules/infantry.yaml +++ b/mods/ra/rules/infantry.yaml @@ -167,6 +167,7 @@ E6: Passenger: PipType: Yellow EngineerRepair: + RepairsBridges: Captures: TakeCover: -AutoTarget: