From 6b1b8d9ed4ddc0f37bc591f07480ae87533394c1 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 8 Aug 2013 21:16:13 +1200 Subject: [PATCH 01/23] Add Order.ExtraData field. --- OpenRA.Game/Network/Order.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/OpenRA.Game/Network/Order.cs b/OpenRA.Game/Network/Order.cs index 90ec7d645a..fd0c4d7c46 100755 --- a/OpenRA.Game/Network/Order.cs +++ b/OpenRA.Game/Network/Order.cs @@ -23,6 +23,7 @@ namespace OpenRA TargetString = 0x04, Queued = 0x08, ExtraLocation = 0x10, + ExtraData = 0x20 } static class OrderFieldsExts @@ -42,12 +43,13 @@ namespace OpenRA public CPos TargetLocation; public string TargetString; public CPos ExtraLocation; + public uint ExtraData; public bool IsImmediate; public Player Player { get { return Subject.Owner; } } Order(string orderString, Actor subject, - Actor targetActor, CPos targetLocation, string targetString, bool queued, CPos extraLocation) + Actor targetActor, CPos targetLocation, string targetString, bool queued, CPos extraLocation, uint extraData) { this.OrderString = orderString; this.Subject = subject; @@ -56,18 +58,19 @@ namespace OpenRA this.TargetString = targetString; this.Queued = queued; this.ExtraLocation = extraLocation; + this.ExtraData = extraData; } // For scripting special powers public Order() - : this(null, null, null, CPos.Zero, null, false, CPos.Zero) { } + : this(null, null, null, CPos.Zero, null, false, CPos.Zero, 0) { } public Order(string orderString, Actor subject, bool queued) - : this(orderString, subject, null, CPos.Zero, null, queued, CPos.Zero) { } + : this(orderString, subject, null, CPos.Zero, null, queued, CPos.Zero, 0) { } public Order(string orderstring, Order order) : this(orderstring, order.Subject, order.TargetActor, order.TargetLocation, - order.TargetString, order.Queued, order.ExtraLocation) {} + order.TargetString, order.Queued, order.ExtraLocation, order.ExtraData) {} public byte[] Serialize() { @@ -102,6 +105,7 @@ namespace OpenRA if (TargetString != null) fields |= OrderFields.TargetString; if (Queued) fields |= OrderFields.Queued; if (ExtraLocation != CPos.Zero) fields |= OrderFields.ExtraLocation; + if (ExtraData != 0) fields |= OrderFields.ExtraData; w.Write((byte)fields); @@ -113,6 +117,8 @@ namespace OpenRA w.Write(TargetString); if (ExtraLocation != CPos.Zero) w.Write(ExtraLocation.ToInt2()); + if (ExtraData != 0) + w.Write(ExtraData); return ret.ToArray(); } @@ -134,12 +140,13 @@ namespace OpenRA var targetString = flags.HasField(OrderFields.TargetString) ? r.ReadString() : null; var queued = flags.HasField(OrderFields.Queued); var extraLocation = (CPos)(flags.HasField(OrderFields.ExtraLocation) ? r.ReadInt2() : int2.Zero); + var extraData = flags.HasField(OrderFields.ExtraData) ? r.ReadUInt32() : 0; Actor subject, targetActor; - if( !TryGetActorFromUInt( world, subjectId, out subject ) || !TryGetActorFromUInt( world, targetActorId, out targetActor ) ) + if (!TryGetActorFromUInt(world, subjectId, out subject) || !TryGetActorFromUInt(world, targetActorId, out targetActor)) return null; - return new Order( order, subject, targetActor, targetLocation, targetString, queued, extraLocation); + return new Order(order, subject, targetActor, targetLocation, targetString, queued, extraLocation, extraData); } case 0xfe: From ff98fb0cdf99a2f39b5965011ba31227198ded42 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 8 Aug 2013 18:46:36 +1200 Subject: [PATCH 02/23] Add ITargetableInfo interface for querying target types. --- OpenRA.Game/Traits/TraitsInterfaces.cs | 5 +++++ OpenRA.Mods.RA/TargetableBuilding.cs | 3 ++- OpenRA.Mods.RA/TargetableUnit.cs | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index a625a5e030..251b2ddbe3 100755 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -204,6 +204,11 @@ namespace OpenRA.Traits } public interface IBodyOrientationInfo {} + public interface ITargetableInfo + { + string[] GetTargetTypes(); + } + public interface ITargetable { string[] TargetTypes { get; } diff --git a/OpenRA.Mods.RA/TargetableBuilding.cs b/OpenRA.Mods.RA/TargetableBuilding.cs index 8bc331338c..6666053472 100755 --- a/OpenRA.Mods.RA/TargetableBuilding.cs +++ b/OpenRA.Mods.RA/TargetableBuilding.cs @@ -15,10 +15,11 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - public class TargetableBuildingInfo : ITraitInfo, Requires + public class TargetableBuildingInfo : ITraitInfo, ITargetableInfo, Requires { public readonly string[] TargetTypes = { }; + public string[] GetTargetTypes() { return TargetTypes; } public object Create(ActorInitializer init) { return new TargetableBuilding(init.self, this); } } diff --git a/OpenRA.Mods.RA/TargetableUnit.cs b/OpenRA.Mods.RA/TargetableUnit.cs index 282e97c8eb..195e8226c7 100755 --- a/OpenRA.Mods.RA/TargetableUnit.cs +++ b/OpenRA.Mods.RA/TargetableUnit.cs @@ -14,10 +14,11 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - public class TargetableUnitInfo : ITraitInfo + public class TargetableUnitInfo : ITraitInfo, ITargetableInfo { public readonly string[] TargetTypes = { }; + public string[] GetTargetTypes() { return TargetTypes; } public virtual object Create(ActorInitializer init) { return new TargetableUnit(init.self, this); } } From fa517b8787b3a521bac414e96c26d118f6c01eb5 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 8 Aug 2013 19:57:21 +1200 Subject: [PATCH 03/23] Change WeaponInfo.EffectivenessAgainst to take ActorInfo. --- OpenRA.Game/GameRules/WeaponInfo.cs | 15 +++++++++------ OpenRA.Mods.RA/Combat.cs | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index 019e869c8b..8eaed63881 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -48,12 +48,15 @@ namespace OpenRA.GameRules [Desc("Whether we should prevent prone response for infantry.")] public readonly bool PreventProne = false; - public float EffectivenessAgainst(Actor self) + public float EffectivenessAgainst(ActorInfo ai) { - var health = self.Info.Traits.GetOrDefault(); - if (health == null) return 0f; - var armor = self.Info.Traits.GetOrDefault(); - if (armor == null || armor.Type == null) return 1; + var health = ai.Traits.GetOrDefault(); + if (health == null) + return 0f; + + var armor = ai.Traits.GetOrDefault(); + if (armor == null || armor.Type == null) + return 1; float versus; return Versus.TryGetValue(armor.Type, out versus) ? versus : 1; @@ -140,7 +143,7 @@ namespace OpenRA.GameRules if (targetable == null || !ValidTargets.Intersect(targetable.TargetTypes).Any()) return false; - if (Warheads.All(w => w.EffectivenessAgainst(a) <= 0)) + if (Warheads.All(w => w.EffectivenessAgainst(a.Info) <= 0)) return false; return true; diff --git a/OpenRA.Mods.RA/Combat.cs b/OpenRA.Mods.RA/Combat.cs index 37b751f09f..c50613a2a0 100755 --- a/OpenRA.Mods.RA/Combat.cs +++ b/OpenRA.Mods.RA/Combat.cs @@ -118,7 +118,7 @@ namespace OpenRA.Mods.RA foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0])) foreach (var unit in world.FindActorsInBox(t, t)) unit.InflictDamage(firedBy, - (int)(warhead.Damage * warhead.EffectivenessAgainst(unit)), warhead); + (int)(warhead.Damage * warhead.EffectivenessAgainst(unit.Info)), warhead); } break; } } @@ -173,7 +173,7 @@ namespace OpenRA.Mods.RA var distance = (int)Math.Max(0, (target.CenterPosition - pos).Length * Game.CellSize / 1024 - health.Radius); var falloff = (float)GetDamageFalloff(distance / warhead.Spread); var rawDamage = (float)(warhead.Damage * modifier * falloff); - var multiplier = (float)warhead.EffectivenessAgainst(target); + var multiplier = (float)warhead.EffectivenessAgainst(target.Info); return (float)(rawDamage * multiplier); } From 4f3c9aa0afbc47af740fa5d820fdb11070df1c68 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 7 Aug 2013 22:51:48 +1200 Subject: [PATCH 04/23] Introduce Target.Type property. --- OpenRA.Game/GameRules/WeaponInfo.cs | 10 ++--- OpenRA.Game/Traits/DrawLineToTarget.cs | 6 ++- OpenRA.Game/Traits/Target.cs | 43 +++++++++++++------ OpenRA.Game/Traits/Util.cs | 2 +- OpenRA.Mods.RA/Activities/Attack.cs | 2 +- OpenRA.Mods.RA/Activities/CaptureActor.cs | 2 +- OpenRA.Mods.RA/Activities/Demolish.cs | 4 +- OpenRA.Mods.RA/Activities/DonateSupplies.cs | 2 +- OpenRA.Mods.RA/Activities/Enter.cs | 2 +- OpenRA.Mods.RA/Activities/Heal.cs | 2 +- OpenRA.Mods.RA/Activities/Infiltrate.cs | 2 +- .../Activities/LegacyCaptureActor.cs | 4 +- OpenRA.Mods.RA/Activities/RepairBridge.cs | 2 +- OpenRA.Mods.RA/Activities/RepairBuilding.cs | 2 +- OpenRA.Mods.RA/Attack/AttackBase.cs | 4 +- OpenRA.Mods.RA/Attack/AttackLeap.cs | 2 +- OpenRA.Mods.RA/Attack/AttackTurreted.cs | 2 +- OpenRA.Mods.RA/Move/Move.cs | 2 +- 18 files changed, 56 insertions(+), 39 deletions(-) diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index 8eaed63881..46ad55b65a 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -151,12 +151,10 @@ namespace OpenRA.GameRules public bool IsValidAgainst(Target target, World world) { - if (!target.IsValid) - return false; - - if (target.IsActor) + if (target.Type == TargetType.Actor) return IsValidAgainst(target.Actor); - else + + if (target.Type == TargetType.Terrain) { var cell = target.CenterPosition.ToCPos(); if (ValidTargets.Contains("Ground") && world.GetTerrainType(cell) != "Water") @@ -167,6 +165,8 @@ namespace OpenRA.GameRules return false; } + + return false; } } } diff --git a/OpenRA.Game/Traits/DrawLineToTarget.cs b/OpenRA.Game/Traits/DrawLineToTarget.cs index a241af6715..bf8baabfb4 100644 --- a/OpenRA.Game/Traits/DrawLineToTarget.cs +++ b/OpenRA.Game/Traits/DrawLineToTarget.cs @@ -109,8 +109,10 @@ namespace OpenRA.Traits self.World.AddFrameEndTask(w => { - if (self.Destroyed) return; - if (target.IsActor && display) + if (self.Destroyed) + return; + + if (target.Type == TargetType.Actor && display) w.Add(new FlashTarget(target.Actor)); var line = self.TraitOrDefault(); diff --git a/OpenRA.Game/Traits/Target.cs b/OpenRA.Game/Traits/Target.cs index 6611dab916..09c177ea13 100644 --- a/OpenRA.Game/Traits/Target.cs +++ b/OpenRA.Game/Traits/Target.cs @@ -14,18 +14,19 @@ using System.Linq; namespace OpenRA.Traits { + public enum TargetType { Invalid, Actor, Terrain } public struct Target { - public static readonly Target[] NoTargets = {}; - public static readonly Target None = new Target(); + public static readonly Target[] None = {}; + public static readonly Target Invalid = new Target { type = TargetType.Invalid }; + TargetType type; Actor actor; WPos pos; - bool valid; int generation; - public static Target FromPos(WPos p) { return new Target { pos = p, valid = true }; } - public static Target FromCell(CPos c) { return new Target { pos = c.CenterPosition, valid = true }; } + public static Target FromPos(WPos p) { return new Target { pos = p, type = TargetType.Terrain }; } + public static Target FromCell(CPos c) { return new Target { pos = c.CenterPosition, type = TargetType.Terrain }; } public static Target FromOrder(Order o) { return o.TargetActor != null @@ -38,23 +39,39 @@ namespace OpenRA.Traits return new Target { actor = a, - valid = (a != null), + type = a != null ? TargetType.Actor : TargetType.Invalid, generation = a.Generation, }; } - public bool IsValid { get { return valid && (actor == null || (actor.IsInWorld && !actor.IsDead() && actor.Generation == generation)); } } - public Actor Actor { get { return IsActor ? actor : null; } } + public bool IsValid { get { return Type != TargetType.Invalid; } } + public Actor Actor { get { return actor; } } - // TODO: This should return true even if the actor is destroyed - public bool IsActor { get { return actor != null && !actor.Destroyed; } } + public TargetType Type + { + get + { + if (type == TargetType.Actor) + { + // Actor is no longer in the world + if (!actor.IsInWorld || actor.IsDead()) + return TargetType.Invalid; + + // Actor generation has changed (teleported or captured) + if (actor.Generation != generation) + return TargetType.Invalid; + } + + return type; + } + } // Representative position - see Positions for the full set of targetable positions. public WPos CenterPosition { get { - if (!IsValid) + if (Type == TargetType.Invalid) throw new InvalidOperationException("Attempting to query the position of an invalid Target"); return actor != null ? actor.CenterPosition : pos; @@ -67,7 +84,7 @@ namespace OpenRA.Traits { get { - if (!IsValid) + if (Type == TargetType.Invalid) return NoPositions; if (actor == null) @@ -83,7 +100,7 @@ namespace OpenRA.Traits public bool IsInRange(WPos origin, WRange range) { - if (!IsValid) + if (Type == TargetType.Invalid) return false; // Target ranges are calculated in 2D, so ignore height differences diff --git a/OpenRA.Game/Traits/Util.cs b/OpenRA.Game/Traits/Util.cs index 02f1bd4d9d..c793944b5f 100755 --- a/OpenRA.Game/Traits/Util.cs +++ b/OpenRA.Game/Traits/Util.cs @@ -134,7 +134,7 @@ namespace OpenRA.Traits public static IEnumerable AdjacentCells(Target target) { - var cells = target.IsActor + var cells = target.Type == TargetType.Actor ? target.Actor.OccupiesSpace.OccupiedCells().Select(c => c.First).ToArray() : new CPos[] { }; diff --git a/OpenRA.Mods.RA/Activities/Attack.cs b/OpenRA.Mods.RA/Activities/Attack.cs index c583115ed8..a6ccc10edb 100755 --- a/OpenRA.Mods.RA/Activities/Attack.cs +++ b/OpenRA.Mods.RA/Activities/Attack.cs @@ -33,7 +33,7 @@ namespace OpenRA.Mods.RA.Activities public Attack(Target target, WRange range, bool allowMovement) { Target = target; - if (target.IsActor) + if (target.Type == TargetType.Actor) targetable = target.Actor.TraitOrDefault(); Range = range; diff --git a/OpenRA.Mods.RA/Activities/CaptureActor.cs b/OpenRA.Mods.RA/Activities/CaptureActor.cs index 91af78a7aa..ee9379d780 100644 --- a/OpenRA.Mods.RA/Activities/CaptureActor.cs +++ b/OpenRA.Mods.RA/Activities/CaptureActor.cs @@ -24,7 +24,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (!target.IsValid) + if (target.Type != TargetType.Actor) return NextActivity; var capturable = target.Actor.Trait(); diff --git a/OpenRA.Mods.RA/Activities/Demolish.cs b/OpenRA.Mods.RA/Activities/Demolish.cs index 2d9a9e0b26..5161cb7509 100644 --- a/OpenRA.Mods.RA/Activities/Demolish.cs +++ b/OpenRA.Mods.RA/Activities/Demolish.cs @@ -27,13 +27,13 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () => { // Can't demolish an already dead actor - if (!target.IsValid) + if (target.Type != TargetType.Actor) return; // Invulnerable actors can't be demolished diff --git a/OpenRA.Mods.RA/Activities/DonateSupplies.cs b/OpenRA.Mods.RA/Activities/DonateSupplies.cs index 5d82168c76..4382f60895 100644 --- a/OpenRA.Mods.RA/Activities/DonateSupplies.cs +++ b/OpenRA.Mods.RA/Activities/DonateSupplies.cs @@ -27,7 +27,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid || !target.IsActor) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; var targetActor = target.Actor; diff --git a/OpenRA.Mods.RA/Activities/Enter.cs b/OpenRA.Mods.RA/Activities/Enter.cs index c4afe104b5..cddb4b1cf2 100755 --- a/OpenRA.Mods.RA/Activities/Enter.cs +++ b/OpenRA.Mods.RA/Activities/Enter.cs @@ -27,7 +27,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; if (!Util.AdjacentCells(target).Any(c => c == self.Location)) diff --git a/OpenRA.Mods.RA/Activities/Heal.cs b/OpenRA.Mods.RA/Activities/Heal.cs index 199f89d041..ea000e910e 100755 --- a/OpenRA.Mods.RA/Activities/Heal.cs +++ b/OpenRA.Mods.RA/Activities/Heal.cs @@ -22,7 +22,7 @@ namespace OpenRA.Mods.RA.Activities protected override Activity InnerTick(Actor self, AttackBase attack) { - if (Target.IsActor && Target.Actor.GetDamageState() == DamageState.Undamaged) + if (Target.Type == TargetType.Actor && Target.Actor.GetDamageState() == DamageState.Undamaged) return NextActivity; return base.InnerTick(self, attack); diff --git a/OpenRA.Mods.RA/Activities/Infiltrate.cs b/OpenRA.Mods.RA/Activities/Infiltrate.cs index 60f1338442..9baad5547f 100644 --- a/OpenRA.Mods.RA/Activities/Infiltrate.cs +++ b/OpenRA.Mods.RA/Activities/Infiltrate.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid || target.Actor.Owner == self.Owner) + if (IsCanceled || target.Type != TargetType.Actor || target.Actor.Owner == self.Owner) return NextActivity; foreach (var t in target.Actor.TraitsImplementing()) diff --git a/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs b/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs index 83d8dbad8d..8c2fb6d2ea 100644 --- a/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs +++ b/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs @@ -23,9 +23,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled) - return NextActivity; - if (!target.IsValid) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; var b = target.Actor.TraitOrDefault(); diff --git a/OpenRA.Mods.RA/Activities/RepairBridge.cs b/OpenRA.Mods.RA/Activities/RepairBridge.cs index 90377a47e1..178eea6ce3 100644 --- a/OpenRA.Mods.RA/Activities/RepairBridge.cs +++ b/OpenRA.Mods.RA/Activities/RepairBridge.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; var hut = target.Actor.Trait(); diff --git a/OpenRA.Mods.RA/Activities/RepairBuilding.cs b/OpenRA.Mods.RA/Activities/RepairBuilding.cs index 8e5e4f4bc6..c5c13fafbe 100644 --- a/OpenRA.Mods.RA/Activities/RepairBuilding.cs +++ b/OpenRA.Mods.RA/Activities/RepairBuilding.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; var health = target.Actor.Trait(); diff --git a/OpenRA.Mods.RA/Attack/AttackBase.cs b/OpenRA.Mods.RA/Attack/AttackBase.cs index 9321698c67..94f2b5d8d0 100644 --- a/OpenRA.Mods.RA/Attack/AttackBase.cs +++ b/OpenRA.Mods.RA/Attack/AttackBase.cs @@ -54,7 +54,7 @@ namespace OpenRA.Mods.RA if (self.IsDisabled()) return false; - if (target.IsActor && target.Actor.HasTrait() && + if (target.Type == TargetType.Actor && target.Actor.HasTrait() && !target.Actor.Trait().TargetableBy(target.Actor,self)) return false; @@ -114,7 +114,7 @@ namespace OpenRA.Mods.RA { if (order is AttackOrderTargeter) { - if (target.IsActor) + if (target.Type == TargetType.Actor) return new Order("Attack", self, queued) { TargetActor = target.Actor }; else return new Order("Attack", self, queued) { TargetLocation = target.CenterPosition.ToCPos() }; diff --git a/OpenRA.Mods.RA/Attack/AttackLeap.cs b/OpenRA.Mods.RA/Attack/AttackLeap.cs index 75683b1673..9bfbed10cf 100644 --- a/OpenRA.Mods.RA/Attack/AttackLeap.cs +++ b/OpenRA.Mods.RA/Attack/AttackLeap.cs @@ -37,7 +37,7 @@ namespace OpenRA.Mods.RA public override void DoAttack(Actor self, Target target) { - if (!CanAttack(self, target) || !target.IsActor) + if (target.Type != TargetType.Actor || !CanAttack(self, target)) return; var a = ChooseArmamentForTarget(target); diff --git a/OpenRA.Mods.RA/Attack/AttackTurreted.cs b/OpenRA.Mods.RA/Attack/AttackTurreted.cs index 06c944ce44..4708273e88 100644 --- a/OpenRA.Mods.RA/Attack/AttackTurreted.cs +++ b/OpenRA.Mods.RA/Attack/AttackTurreted.cs @@ -67,7 +67,7 @@ namespace OpenRA.Mods.RA base.ResolveOrder(self, order); if (order.OrderString == "Stop") - target = Target.None; + target = Target.Invalid; } public virtual void BuildingComplete(Actor self) { buildComplete = true; } diff --git a/OpenRA.Mods.RA/Move/Move.cs b/OpenRA.Mods.RA/Move/Move.cs index e3d8e79e0e..503b0bd2c8 100755 --- a/OpenRA.Mods.RA/Move/Move.cs +++ b/OpenRA.Mods.RA/Move/Move.cs @@ -258,7 +258,7 @@ namespace OpenRA.Mods.RA.Move return Enumerable.Reverse(path).Select(c => Target.FromCell(c)); if (destination != null) return new Target[] { Target.FromCell(destination.Value) }; - return Target.NoTargets; + return Target.None; } abstract class MovePart : Activity From e4d1c654edfb47e467abf15f61157755b68d8c4b Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 7 Aug 2013 23:23:22 +1200 Subject: [PATCH 05/23] Merge IOrderTargeter.CanTargetActor and CanTargetLocation. --- OpenRA.Game/Orders/UnitOrderGenerator.cs | 8 +++---- OpenRA.Game/Traits/TraitsInterfaces.cs | 3 +-- OpenRA.Mods.RA/Air/Aircraft.cs | 10 ++++----- OpenRA.Mods.RA/Attack/AttackBase.cs | 15 +++++++++++-- OpenRA.Mods.RA/Captures.cs | 3 --- OpenRA.Mods.RA/EngineerRepair.cs | 3 --- OpenRA.Mods.RA/Harvester.cs | 9 ++++---- OpenRA.Mods.RA/Infiltrates.cs | 3 --- OpenRA.Mods.RA/LegacyCaptures.cs | 2 -- OpenRA.Mods.RA/Minelayer.cs | 12 +++++----- OpenRA.Mods.RA/Move/Mobile.cs | 9 ++++---- OpenRA.Mods.RA/Orders/DeployOrderTargeter.cs | 11 +++++----- .../Orders/EnterBuildingOrderTargeter.cs | 4 +--- OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs | 22 +++++++------------ OpenRA.Mods.RA/RallyPoint.cs | 9 ++++---- OpenRA.Mods.RA/RepairsBridges.cs | 3 --- OpenRA.Mods.RA/SupplyTruck.cs | 3 --- 17 files changed, 53 insertions(+), 76 deletions(-) diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index 2c2a75950d..7ee5e66689 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -96,11 +96,9 @@ namespace OpenRA.Orders modifiers |= TargetModifiers.ForceMove; string cursor = null; - if (underCursor != null) - if (o.Order.CanTargetActor(self, underCursor, modifiers, ref cursor)) - return new UnitOrderResult(self, o.Order, o.Trait, cursor, Target.FromActor(underCursor)); - if (o.Order.CanTargetLocation(self, xy, actorsAt, modifiers, ref cursor)) - return new UnitOrderResult(self, o.Order, o.Trait, cursor, Target.FromCell(xy)); + var target = underCursor != null ? Target.FromActor(underCursor) : Target.FromCell(xy); + if (o.Order.CanTarget(self, target, actorsAt, modifiers, ref cursor)) + return new UnitOrderResult(self, o.Order, o.Trait, cursor, target); } } diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 251b2ddbe3..6b434464e7 100755 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -55,8 +55,7 @@ namespace OpenRA.Traits { string OrderID { get; } int OrderPriority { get; } - bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor); - bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor); + bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor); bool IsQueued { get; } } diff --git a/OpenRA.Mods.RA/Air/Aircraft.cs b/OpenRA.Mods.RA/Air/Aircraft.cs index b9b407b1c4..9240cb9b3e 100755 --- a/OpenRA.Mods.RA/Air/Aircraft.cs +++ b/OpenRA.Mods.RA/Air/Aircraft.cs @@ -217,15 +217,13 @@ namespace OpenRA.Mods.RA.Air public string OrderID { get { return "Move"; } } public int OrderPriority { get { return 4; } } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - return false; - } + if (target.Type != TargetType.Terrain) + return false; - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - cursor = self.World.Map.IsInMap(location) ? "move" : "move-blocked"; + cursor = self.World.Map.IsInMap(target.CenterPosition.ToCPos()) ? "move" : "move-blocked"; return true; } diff --git a/OpenRA.Mods.RA/Attack/AttackBase.cs b/OpenRA.Mods.RA/Attack/AttackBase.cs index 94f2b5d8d0..7406e0be06 100644 --- a/OpenRA.Mods.RA/Attack/AttackBase.cs +++ b/OpenRA.Mods.RA/Attack/AttackBase.cs @@ -169,7 +169,7 @@ namespace OpenRA.Mods.RA public string OrderID { get; private set; } public int OrderPriority { get; private set; } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); @@ -192,7 +192,7 @@ namespace OpenRA.Mods.RA return self.Owner.Stances[target.Owner] == targetableRelationship; } - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) + bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) { if (!self.World.Map.IsInMap(location)) return false; @@ -214,6 +214,17 @@ namespace OpenRA.Mods.RA return false; } + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) + { + if (target.Type == TargetType.Actor) + return CanTargetActor(self, target.Actor, modifiers, ref cursor); + + if (target.Type == TargetType.Terrain) + return CanTargetLocation(self, target.CenterPosition.ToCPos(), othersAtTarget, modifiers, ref cursor); + + return false; + } + public bool IsQueued { get; protected set; } } } diff --git a/OpenRA.Mods.RA/Captures.cs b/OpenRA.Mods.RA/Captures.cs index 093bfec04a..cc8242fd68 100644 --- a/OpenRA.Mods.RA/Captures.cs +++ b/OpenRA.Mods.RA/Captures.cs @@ -96,9 +96,6 @@ namespace OpenRA.Mods.RA public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; - var canTargetActor = useCaptureCursor(target); cursor = canTargetActor ? "ability" : "move-blocked"; diff --git a/OpenRA.Mods.RA/EngineerRepair.cs b/OpenRA.Mods.RA/EngineerRepair.cs index 785c5029a6..9d2c6671b3 100644 --- a/OpenRA.Mods.RA/EngineerRepair.cs +++ b/OpenRA.Mods.RA/EngineerRepair.cs @@ -59,9 +59,6 @@ namespace OpenRA.Mods.RA public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; - if (!target.HasTrait()) return false; diff --git a/OpenRA.Mods.RA/Harvester.cs b/OpenRA.Mods.RA/Harvester.cs index ec1860261d..a2126cda1d 100644 --- a/OpenRA.Mods.RA/Harvester.cs +++ b/OpenRA.Mods.RA/Harvester.cs @@ -419,16 +419,15 @@ namespace OpenRA.Mods.RA public int OrderPriority { get { return 10; } } public bool IsQueued { get; protected set; } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - return false; - } + if (target.Type != TargetType.Terrain) + return false; - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { if (modifiers.HasModifier(TargetModifiers.ForceMove)) return false; + var location = target.CenterPosition.ToCPos(); // Don't leak info about resources under the shroud if (!self.Owner.Shroud.IsExplored(location)) return false; diff --git a/OpenRA.Mods.RA/Infiltrates.cs b/OpenRA.Mods.RA/Infiltrates.cs index 195b355fde..d7ae411874 100644 --- a/OpenRA.Mods.RA/Infiltrates.cs +++ b/OpenRA.Mods.RA/Infiltrates.cs @@ -95,9 +95,6 @@ namespace OpenRA.Mods.RA public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; - if (!target.HasTrait()) return false; diff --git a/OpenRA.Mods.RA/LegacyCaptures.cs b/OpenRA.Mods.RA/LegacyCaptures.cs index 32a0602bc4..5c393fa796 100644 --- a/OpenRA.Mods.RA/LegacyCaptures.cs +++ b/OpenRA.Mods.RA/LegacyCaptures.cs @@ -94,8 +94,6 @@ namespace OpenRA.Mods.RA public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) return false; - var canTargetActor = useCaptureCursor(target); if (canTargetActor) diff --git a/OpenRA.Mods.RA/Minelayer.cs b/OpenRA.Mods.RA/Minelayer.cs index 2030368a42..a8f56b9913 100644 --- a/OpenRA.Mods.RA/Minelayer.cs +++ b/OpenRA.Mods.RA/Minelayer.cs @@ -156,21 +156,21 @@ namespace OpenRA.Mods.RA public string OrderID { get { return "BeginMinefield"; } } public int OrderPriority { get { return 5; } } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - return false; - } + if (target.Type != TargetType.Terrain) + return false; - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { + var location = target.CenterPosition.ToCPos(); if (!self.World.Map.IsInMap(location)) return false; cursor = "ability"; IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - return (actorsAtLocation.Count == 0 && modifiers.HasModifier(TargetModifiers.ForceAttack)); + return !othersAtTarget.Any() && modifiers.HasModifier(TargetModifiers.ForceAttack); } + public bool IsQueued { get; protected set; } } } diff --git a/OpenRA.Mods.RA/Move/Mobile.cs b/OpenRA.Mods.RA/Move/Mobile.cs index 0da1ec50b1..1b9217f3d0 100755 --- a/OpenRA.Mods.RA/Move/Mobile.cs +++ b/OpenRA.Mods.RA/Move/Mobile.cs @@ -494,13 +494,12 @@ namespace OpenRA.Mods.RA.Move public int OrderPriority { get { return 4; } } public bool IsQueued { get; protected set; } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - return false; - } + if (!target.IsValid) + return false; - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { + var location = target.CenterPosition.ToCPos(); IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); cursor = "move"; diff --git a/OpenRA.Mods.RA/Orders/DeployOrderTargeter.cs b/OpenRA.Mods.RA/Orders/DeployOrderTargeter.cs index 88491e5e83..bd28e219af 100755 --- a/OpenRA.Mods.RA/Orders/DeployOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/DeployOrderTargeter.cs @@ -35,16 +35,15 @@ namespace OpenRA.Mods.RA.Orders public string OrderID { get; private set; } public int OrderPriority { get; private set; } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { + if (target.Type != TargetType.Actor) + return false; + IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); cursor = useDeployCursor() ? "deploy" : "deploy-blocked"; - return self == target; - } - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { - return false; + return self == target.Actor; } public bool IsQueued { get; protected set; } diff --git a/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs b/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs index e679830493..26f151a64c 100755 --- a/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs @@ -9,6 +9,7 @@ #endregion using System; +using System.Collections.Generic; using OpenRA.Traits; namespace OpenRA.Mods.RA.Orders @@ -28,9 +29,6 @@ namespace OpenRA.Mods.RA.Orders public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; - if (!target.HasTrait()) return false; diff --git a/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs b/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs index 2f19c71a2b..52e51121a6 100755 --- a/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs @@ -15,7 +15,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA.Orders { - public class UnitOrderTargeter : IOrderTargeter + public abstract class UnitOrderTargeter : IOrderTargeter { readonly string cursor; readonly bool targetEnemyUnits, targetAllyUnits; @@ -33,27 +33,24 @@ namespace OpenRA.Mods.RA.Orders public int OrderPriority { get; private set; } public bool? ForceAttack = null; - public virtual bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public abstract bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor); + + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - if( self == null ) throw new ArgumentNullException( "self" ); - if( target == null ) throw new ArgumentNullException( "target" ); + if (target.Type != TargetType.Actor) + return false; cursor = this.cursor; IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); if (ForceAttack != null && modifiers.HasModifier(TargetModifiers.ForceAttack) != ForceAttack) return false; - var playerRelationship = self.Owner.Stances[target.Owner]; + var playerRelationship = self.Owner.Stances[target.Actor.Owner]; if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Ally && !targetAllyUnits) return false; if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Enemy && !targetEnemyUnits) return false; - return true; - } - - public virtual bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { - return false; + return CanTargetActor(self, target.Actor, modifiers, ref cursor); } public virtual bool IsQueued { get; protected set; } @@ -71,9 +68,6 @@ namespace OpenRA.Mods.RA.Orders public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; - if (!target.TraitsImplementing().Any(t => t.TargetTypes.Contains(targetType))) return false; diff --git a/OpenRA.Mods.RA/RallyPoint.cs b/OpenRA.Mods.RA/RallyPoint.cs index 1db32f3e2b..6d29981e99 100755 --- a/OpenRA.Mods.RA/RallyPoint.cs +++ b/OpenRA.Mods.RA/RallyPoint.cs @@ -58,13 +58,12 @@ namespace OpenRA.Mods.RA public string OrderID { get { return "SetRallyPoint"; } } public int OrderPriority { get { return 0; } } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - return false; - } + if (target.Type != TargetType.Terrain) + return false; - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { + var location = target.CenterPosition.ToCPos(); if (self.World.Map.IsInMap(location)) { cursor = "ability"; diff --git a/OpenRA.Mods.RA/RepairsBridges.cs b/OpenRA.Mods.RA/RepairsBridges.cs index 2e8ab2cf14..27024629b7 100644 --- a/OpenRA.Mods.RA/RepairsBridges.cs +++ b/OpenRA.Mods.RA/RepairsBridges.cs @@ -71,9 +71,6 @@ namespace OpenRA.Mods.RA public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; - var bridge = target.TraitOrDefault(); if (bridge == null) return false; diff --git a/OpenRA.Mods.RA/SupplyTruck.cs b/OpenRA.Mods.RA/SupplyTruck.cs index b7b779caef..7e591a3795 100644 --- a/OpenRA.Mods.RA/SupplyTruck.cs +++ b/OpenRA.Mods.RA/SupplyTruck.cs @@ -72,9 +72,6 @@ namespace OpenRA.Mods.RA public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; - if (target.AppearsHostileTo(self)) return false; From 0a9d920eb6ce7e98af23b409cac0f1020923ae83 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 7 Aug 2013 13:53:32 +1200 Subject: [PATCH 06/23] Allow world and (render-) player actors to render. --- OpenRA.Game/Graphics/WorldRenderer.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index fe75a8fa0c..bfd1a91990 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -73,9 +73,15 @@ namespace OpenRA.Graphics var bounds = Game.viewport.WorldBounds(world); var comparer = new RenderableComparer(this); - var actors = world.FindActorsInBox( - bounds.TopLeftAsCPos(), - bounds.BottomRightAsCPos()); + var tl = bounds.TopLeftAsCPos(); + var br = bounds.BottomRightAsCPos(); + var actors = world.FindActorsInBox(tl, br) + .Append(world.WorldActor) + .ToList(); + + // Include player actor for the rendered player + if (world.RenderPlayer != null) + actors.Add(world.RenderPlayer.PlayerActor); var worldRenderables = actors.SelectMany(a => a.Render(this)); if (world.OrderGenerator != null) From c3bcca2ff74c9dbc7e68ecb3eaa34afde46eebef Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 11 Aug 2013 15:56:32 +1200 Subject: [PATCH 07/23] Use selection priority when picking tooltip actor. --- OpenRA.Game/Orders/UnitOrderGenerator.cs | 8 ++++---- OpenRA.Game/Widgets/ViewportControllerWidget.cs | 15 ++++++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index 7ee5e66689..26f1a79ece 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -21,7 +21,7 @@ namespace OpenRA.Orders { var underCursor = world.FindUnitsAtMouse(mi.Location) .Where(a => a.HasTrait()) - .OrderByDescending(a => a.SelectionPriority()) + .OrderByDescending(a => a.Info.SelectionPriority()) .FirstOrDefault(); var orders = world.Selection.Actors @@ -51,7 +51,7 @@ namespace OpenRA.Orders var underCursor = world.FindUnitsAtMouse(mi.Location) .Where(a => a.HasTrait()) - .OrderByDescending(a => a.SelectionPriority()) + .OrderByDescending(a => a.Info.SelectionPriority()) .FirstOrDefault(); if (underCursor != null && (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())) @@ -135,9 +135,9 @@ namespace OpenRA.Orders public static class SelectableExts { - public static int SelectionPriority(this Actor a) + public static int SelectionPriority(this ActorInfo a) { - var selectableInfo = a.Info.Traits.GetOrDefault(); + var selectableInfo = a.Traits.GetOrDefault(); return selectableInfo != null ? selectableInfo.Priority : int.MinValue; } } diff --git a/OpenRA.Game/Widgets/ViewportControllerWidget.cs b/OpenRA.Game/Widgets/ViewportControllerWidget.cs index 23a1a597ca..7a3ed4b510 100755 --- a/OpenRA.Game/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Game/Widgets/ViewportControllerWidget.cs @@ -14,6 +14,7 @@ using System.Linq; using OpenRA.FileFormats; using OpenRA.GameRules; using OpenRA.Graphics; +using OpenRA.Orders; using OpenRA.Traits; namespace OpenRA.Widgets @@ -101,13 +102,17 @@ namespace OpenRA.Widgets return; } - var actor = world.FindUnitsAtMouse(Viewport.LastMousePos).FirstOrDefault(); - if (actor == null) - return; + var underCursor = world.FindUnitsAtMouse(Viewport.LastMousePos) + .Where(a => a.HasTrait()) + .OrderByDescending(a => a.Info.SelectionPriority()) + .FirstOrDefault(); - ActorTooltip = actor.TraitsImplementing().FirstOrDefault(); - if (ActorTooltip != null) + if (underCursor != null) + { + ActorTooltip = underCursor.TraitsImplementing().First(); TooltipType = WorldTooltipType.Actor; + return; + } } public static string GetScrollCursor(Widget w, ScrollDirection edge, int2 pos) From eef941fd42c2235b1412e0def7ef39764889e447 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 7 Aug 2013 13:48:23 +1200 Subject: [PATCH 08/23] Track per-player frozen actors. --- OpenRA.Game/OpenRA.Game.csproj | 1 + OpenRA.Game/Traits/DrawLineToTarget.cs | 18 ++ OpenRA.Game/Traits/Player/FrozenActorLayer.cs | 173 ++++++++++++++++++ OpenRA.Game/WorldUtils.cs | 14 ++ OpenRA.Mods.RA/Effects/FrozenActorProxy.cs | 53 ------ OpenRA.Mods.RA/Modifiers/FrozenUnderFog.cs | 87 +++++++-- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 1 - mods/cnc/rules/system.yaml | 1 + mods/d2k/rules/system.yaml | 2 +- mods/ra/rules/system.yaml | 1 + 10 files changed, 277 insertions(+), 74 deletions(-) create mode 100755 OpenRA.Game/Traits/Player/FrozenActorLayer.cs delete mode 100644 OpenRA.Mods.RA/Effects/FrozenActorProxy.cs diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index e3b4c1c2d7..e44917e4c5 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -234,6 +234,7 @@ + diff --git a/OpenRA.Game/Traits/DrawLineToTarget.cs b/OpenRA.Game/Traits/DrawLineToTarget.cs index bf8baabfb4..319bf94fce 100644 --- a/OpenRA.Game/Traits/DrawLineToTarget.cs +++ b/OpenRA.Game/Traits/DrawLineToTarget.cs @@ -120,6 +120,24 @@ namespace OpenRA.Traits line.SetTarget(self, target, color, display); }); } + + public static void SetTargetLine(this Actor self, FrozenActor target, Color color, bool display) + { + if (self.Owner != self.World.LocalPlayer) + return; + + self.World.AddFrameEndTask(w => + { + if (self.Destroyed) + return; + + target.Flash(); + + var line = self.TraitOrDefault(); + if (line != null) + line.SetTarget(self, Target.FromPos(target.CenterPosition), color, display); + }); + } } } diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs new file mode 100755 index 0000000000..850d69d865 --- /dev/null +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -0,0 +1,173 @@ +#region Copyright & License Information +/* + * Copyright 2007-2013 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; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Traits +{ + public class FrozenActorLayerInfo : ITraitInfo + { + [Desc("Size of partition bins (screen pixels)")] + public readonly int BinSize = 250; + + public object Create(ActorInitializer init) { return new FrozenActorLayer(init.world, this); } + } + + public class FrozenActor + { + public readonly IEnumerable Footprint; + public readonly WPos CenterPosition; + public readonly Rectangle Bounds; + readonly Actor actor; + + public IRenderable[] Renderables { set; private get; } + public Player Owner; + + public string TooltipName; + public Player TooltipOwner; + + public int HP; + public DamageState DamageState; + + public bool Visible; + + public FrozenActor(Actor self, IEnumerable footprint) + { + actor = self; + Footprint = footprint; + CenterPosition = self.CenterPosition; + Bounds = self.Bounds.Value; + } + + public uint ID { get { return actor.ActorID; } } + public bool IsValid { get { return Owner != null; } } + public ActorInfo Info { get { return actor.Info; } } + public Actor Actor { get { return !actor.IsDead() ? actor : null; } } + + int flashTicks; + public void Tick(World world, Shroud shroud) + { + Visible = !Footprint.Any(c => shroud.IsVisible(c)); + + if (flashTicks > 0) + flashTicks--; + } + + public void Flash() + { + flashTicks = 5; + } + + public IEnumerable Render(WorldRenderer wr) + { + if (Renderables == null) + return SpriteRenderable.None; + + if (flashTicks > 0 && flashTicks % 2 == 0) + { + var highlight = wr.Palette("highlight"); + return Renderables.Concat(Renderables.Where(r => !r.IsDecoration) + .Select(r => r.WithPalette(highlight))); + } + return Renderables; + } + } + + public class FrozenActorLayer : IRender, ITick, ISync + { + [Sync] public int VisibilityHash; + [Sync] public int FrozenHash; + + readonly FrozenActorLayerInfo info; + Dictionary frozen; + List[,] bins; + + public FrozenActorLayer(World world, FrozenActorLayerInfo info) + { + this.info = info; + frozen = new Dictionary(); + bins = new List[ + world.Map.MapSize.X * Game.CellSize / info.BinSize, + world.Map.MapSize.Y * Game.CellSize / info.BinSize]; + + for (var j = 0; j <= bins.GetUpperBound(1); j++) + for (var i = 0; i <= bins.GetUpperBound(0); i++) + bins[i, j] = new List(); + } + + public void Add(FrozenActor fa) + { + frozen.Add(fa.ID, fa); + + var top = (int)Math.Max(0, fa.Bounds.Top / info.BinSize); + var left = (int)Math.Max(0, fa.Bounds.Left / info.BinSize); + var bottom = (int)Math.Min(bins.GetUpperBound(1), fa.Bounds.Bottom / info.BinSize); + var right = (int)Math.Min(bins.GetUpperBound(0), fa.Bounds.Right / info.BinSize); + for (var j = top; j <= bottom; j++) + for (var i = left; i <= right; i++) + bins[i, j].Add(fa); + } + + public void Tick(Actor self) + { + var remove = new List(); + VisibilityHash = 0; + FrozenHash = 0; + + foreach (var kv in frozen) + { + FrozenHash += (int)kv.Key; + + kv.Value.Tick(self.World, self.Owner.Shroud); + if (kv.Value.Visible) + VisibilityHash += (int)kv.Key; + + if (!kv.Value.Visible && kv.Value.Actor == null) + remove.Add(kv.Key); + } + + foreach (var r in remove) + { + foreach (var bin in bins) + bin.Remove(frozen[r]); + frozen.Remove(r); + } + } + + public virtual IEnumerable Render(Actor self, WorldRenderer wr) + { + return frozen.Values + .Where(f => f.Visible) + .SelectMany(ff => ff.Render(wr)); + } + + public IEnumerable FrozenActorsAt(int2 pxPos) + { + var x = (pxPos.X / info.BinSize).Clamp(0, bins.GetUpperBound(0)); + var y = (pxPos.Y / info.BinSize).Clamp(0, bins.GetUpperBound(1)); + return bins[x, y].Where(p => p.Bounds.Contains(pxPos) && p.IsValid); + } + + public FrozenActor FromID(uint id) + { + FrozenActor ret; + if (!frozen.TryGetValue(id, out ret)) + return null; + + return ret; + } + } +} diff --git a/OpenRA.Game/WorldUtils.cs b/OpenRA.Game/WorldUtils.cs index 965c6d8d33..94f635ba1d 100755 --- a/OpenRA.Game/WorldUtils.cs +++ b/OpenRA.Game/WorldUtils.cs @@ -27,6 +27,20 @@ namespace OpenRA return FindActorsInBox(world, loc, loc).Where(a => !world.FogObscures(a)); } + public static readonly IEnumerable NoFrozenActors = new FrozenActor[0].AsEnumerable(); + public static IEnumerable FindFrozenActorsAtMouse(this World world, int2 mouseLocation) + { + if (world.RenderPlayer == null) + return NoFrozenActors; + + var frozenLayer = world.RenderPlayer.PlayerActor.TraitOrDefault(); + if (frozenLayer == null) + return NoFrozenActors; + + var loc = Game.viewport.ViewToWorldPx(mouseLocation).ToInt2(); + return frozenLayer.FrozenActorsAt(loc); + } + public static IEnumerable FindActorsInBox(this World world, CPos tl, CPos br) { return world.FindActorsInBox(tl.TopLeft, br.BottomRight); diff --git a/OpenRA.Mods.RA/Effects/FrozenActorProxy.cs b/OpenRA.Mods.RA/Effects/FrozenActorProxy.cs deleted file mode 100644 index 96fe2d82e0..0000000000 --- a/OpenRA.Mods.RA/Effects/FrozenActorProxy.cs +++ /dev/null @@ -1,53 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2013 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.Linq; -using OpenRA.Effects; -using OpenRA.Graphics; -using OpenRA.Traits; - -namespace OpenRA.Mods.RA.Effects -{ - public class FrozenActorProxy : IEffect - { - readonly Actor self; - readonly IEnumerable footprint; - IRenderable[] renderables; - - public FrozenActorProxy(Actor self, IEnumerable footprint) - { - this.self = self; - this.footprint = footprint; - } - - public void Tick(World world) { } - public void SetRenderables(IEnumerable r) - { - renderables = r.Select(rr => rr).ToArray(); - } - - public IEnumerable Render(WorldRenderer wr) - { - if (renderables == null) - return SpriteRenderable.None; - - if (footprint.Any(c => !wr.world.FogObscures(c))) - { - if (self.Destroyed) - self.World.AddFrameEndTask(w => w.Remove(this)); - - return SpriteRenderable.None; - } - - return renderables; - } - } -} diff --git a/OpenRA.Mods.RA/Modifiers/FrozenUnderFog.cs b/OpenRA.Mods.RA/Modifiers/FrozenUnderFog.cs index 8544ae2ffa..78a7ffa5f9 100644 --- a/OpenRA.Mods.RA/Modifiers/FrozenUnderFog.cs +++ b/OpenRA.Mods.RA/Modifiers/FrozenUnderFog.cs @@ -9,7 +9,9 @@ #endregion using System.Collections.Generic; +using System.Drawing; using System.Linq; +using OpenRA.FileFormats; using OpenRA.Graphics; using OpenRA.Mods.RA.Buildings; using OpenRA.Mods.RA.Effects; @@ -24,50 +26,97 @@ namespace OpenRA.Mods.RA public object Create(ActorInitializer init) { return new FrozenUnderFog(init, this); } } - public class FrozenUnderFog : IRenderModifier, IVisibilityModifier, ITickRender + public class FrozenUnderFog : IRenderModifier, IVisibilityModifier, ITick, ITickRender, ISync { - FrozenActorProxy proxy; + [Sync] public int VisibilityHash; + + bool initialized, startsRevealed; IEnumerable footprint; - bool visible, cacheFirstFrame; + Lazy tooltip; + Lazy health; + + Dictionary visible; + Dictionary frozen; public FrozenUnderFog(ActorInitializer init, FrozenUnderFogInfo info) { - footprint = FootprintUtils.Tiles(init.self); - proxy = new FrozenActorProxy(init.self, footprint); - init.world.AddFrameEndTask(w => w.Add(proxy)); - // Spawned actors (e.g. building husks) shouldn't be revealed - cacheFirstFrame = info.StartsRevealed && !init.Contains(); + startsRevealed = info.StartsRevealed && !init.Contains(); + footprint = FootprintUtils.Tiles(init.self); + tooltip = Lazy.New(() => init.self.TraitsImplementing().FirstOrDefault()); + tooltip = Lazy.New(() => init.self.TraitsImplementing().FirstOrDefault()); + health = Lazy.New(() => init.self.TraitOrDefault()); + + frozen = new Dictionary(); + visible = init.world.Players.ToDictionary(p => p, p => false); } public bool IsVisible(Actor self, Player byPlayer) { - return byPlayer == null || footprint.Any(c => byPlayer.Shroud.IsVisible(c)); + return byPlayer == null || visible[byPlayer]; } - public void TickRender(WorldRenderer wr, Actor self) + public void Tick(Actor self) { if (self.Destroyed) return; - visible = IsVisible(self, self.World.RenderPlayer); - - if (cacheFirstFrame) + VisibilityHash = 0; + foreach (var p in self.World.Players) { - visible = true; - cacheFirstFrame = false; + visible[p] = footprint.Any(c => p.Shroud.IsVisible(c)); + if (visible[p]) + VisibilityHash += p.ClientIndex; } - if (visible) + if (!initialized) { - var comparer = new RenderableComparer(wr); - proxy.SetRenderables(self.Render(wr).OrderBy(r => r, comparer)); + foreach (var p in self.World.Players) + { + visible[p] |= startsRevealed; + frozen[p] = new FrozenActor(self, footprint); + p.PlayerActor.Trait().Add(frozen[p]); + } + + initialized = true; } + + foreach (var player in self.World.Players) + { + if (!visible[player]) + continue; + + frozen[player].Owner = self.Owner; + + if (health.Value != null) + { + frozen[player].HP = health.Value.HP; + frozen[player].DamageState = health.Value.DamageState; + } + + if (tooltip.Value != null) + { + frozen[player].TooltipName = tooltip.Value.Name(); + frozen[player].TooltipOwner = tooltip.Value.Owner(); + } + } + } + + public void TickRender(WorldRenderer wr, Actor self) + { + if (self.Destroyed || !initialized || !visible.Any(v => v.Value)) + return; + + // Force a copy of the underlying data + var renderables = self.Render(wr).Select(rr => rr).ToArray(); + foreach (var player in self.World.Players) + if (visible[player]) + frozen[player].Renderables = renderables; } public IEnumerable ModifyRender(Actor self, WorldRenderer wr, IEnumerable r) { - return visible ? r : SpriteRenderable.None; + return IsVisible(self, self.World.RenderPlayer) ? r : SpriteRenderable.None; } } } \ No newline at end of file diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 3dc9d57449..3bf6aed2f6 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -462,7 +462,6 @@ - diff --git a/mods/cnc/rules/system.yaml b/mods/cnc/rules/system.yaml index 99c425c102..01ba2734af 100644 --- a/mods/cnc/rules/system.yaml +++ b/mods/cnc/rules/system.yaml @@ -203,6 +203,7 @@ Player: BaseAttackNotifier: Shroud: PlayerStatistics: + FrozenActorLayer: World: LoadWidgetAtGameStart: diff --git a/mods/d2k/rules/system.yaml b/mods/d2k/rules/system.yaml index 2d6a543416..23da9915c6 100644 --- a/mods/d2k/rules/system.yaml +++ b/mods/d2k/rules/system.yaml @@ -269,7 +269,7 @@ Player: RemapIndex: 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240 BaseAttackNotifier: Shroud: - Shroud: false + FrozenActorLayer: HarvesterAttackNotifier: PlayerStatistics: diff --git a/mods/ra/rules/system.yaml b/mods/ra/rules/system.yaml index 6529debd7f..6be1a8d901 100644 --- a/mods/ra/rules/system.yaml +++ b/mods/ra/rules/system.yaml @@ -524,6 +524,7 @@ Player: RemapIndex: 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 GpsWatcher: Shroud: + FrozenActorLayer: BaseAttackNotifier: PlayerStatistics: From c0cb52a5caa8f5a6cdabf85d8ebee5f113daf09e Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 7 Aug 2013 13:48:23 +1200 Subject: [PATCH 09/23] Show tooltips for frozen actors. --- .../Widgets/ViewportControllerWidget.cs | 14 +++++++++++- .../Widgets/Logic/WorldTooltipLogic.cs | 22 +++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/OpenRA.Game/Widgets/ViewportControllerWidget.cs b/OpenRA.Game/Widgets/ViewportControllerWidget.cs index 7a3ed4b510..25b4970904 100755 --- a/OpenRA.Game/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Game/Widgets/ViewportControllerWidget.cs @@ -19,7 +19,7 @@ using OpenRA.Traits; namespace OpenRA.Widgets { - public enum WorldTooltipType { None, Unexplored, Actor } + public enum WorldTooltipType { None, Unexplored, Actor, FrozenActor } public class ViewportControllerWidget : Widget { @@ -29,6 +29,7 @@ namespace OpenRA.Widgets public WorldTooltipType TooltipType { get; private set; } public IToolTip ActorTooltip { get; private set; } + public FrozenActor FrozenActorTooltip { get; private set; } public int EdgeScrollThreshold = 15; public int EdgeCornerScrollThreshold = 35; @@ -113,6 +114,17 @@ namespace OpenRA.Widgets TooltipType = WorldTooltipType.Actor; return; } + + var frozen = world.FindFrozenActorsAtMouse(Viewport.LastMousePos) + .Where(a => a.TooltipName != null) + .OrderByDescending(a => a.Info.SelectionPriority()) + .FirstOrDefault(); + + if (frozen != null) + { + FrozenActorTooltip = frozen; + TooltipType = WorldTooltipType.FrozenActor; + } } public static string GetScrollCursor(Widget w, ScrollDirection edge, int2 pos) diff --git a/OpenRA.Mods.RA/Widgets/Logic/WorldTooltipLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/WorldTooltipLogic.cs index e34eb34ead..4e946b1286 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/WorldTooltipLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/WorldTooltipLogic.cs @@ -41,16 +41,30 @@ namespace OpenRA.Mods.RA.Widgets.Logic if (viewport == null || viewport.TooltipType == WorldTooltipType.None) return; - labelText = viewport.TooltipType == WorldTooltipType.Unexplored ? "Unexplored Terrain" : - viewport.ActorTooltip.Name(); + Player o = null; + switch (viewport.TooltipType) + { + case WorldTooltipType.Unexplored: + labelText = "Unexplored Terrain"; + break; + case WorldTooltipType.Actor: + labelText = viewport.ActorTooltip.Name(); + o = viewport.ActorTooltip.Owner(); + break; + case WorldTooltipType.FrozenActor: + labelText = viewport.FrozenActorTooltip.TooltipName; + o = viewport.FrozenActorTooltip.TooltipOwner; + break; + } + var textWidth = font.Measure(labelText).X; if (textWidth != cachedWidth) { label.Bounds.Width = textWidth; widget.Bounds.Width = 2*label.Bounds.X + textWidth; } - var o = viewport.ActorTooltip != null ? viewport.ActorTooltip.Owner() : null; - showOwner = viewport.TooltipType == WorldTooltipType.Actor && o != null && !o.NonCombatant; + + showOwner = o != null && !o.NonCombatant; if (showOwner) { From 40a9caddc7e849ef725a8b9b130dad21dc63d78e Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 8 Aug 2013 14:34:49 +1200 Subject: [PATCH 10/23] Add FrozenActor target type. --- OpenRA.Game/GameRules/WeaponInfo.cs | 16 ++++++++++ OpenRA.Game/Traits/Target.cs | 46 ++++++++++++++++++++--------- OpenRA.Game/Traits/Util.cs | 8 +---- OpenRA.Mods.RA/Activities/Attack.cs | 5 ++-- OpenRA.Mods.RA/Attack/AttackBase.cs | 12 ++++---- 5 files changed, 59 insertions(+), 28 deletions(-) diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index 46ad55b65a..91efe67c56 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -149,11 +149,27 @@ namespace OpenRA.GameRules return true; } + public bool IsValidAgainst(FrozenActor a) + { + var targetable = a.Info.Traits.GetOrDefault(); + if (targetable == null || !ValidTargets.Intersect(targetable.GetTargetTypes()).Any()) + return false; + + if (Warheads.All(w => w.EffectivenessAgainst(a.Info) <= 0)) + return false; + + return true; + } + + public bool IsValidAgainst(Target target, World world) { if (target.Type == TargetType.Actor) return IsValidAgainst(target.Actor); + if (target.Type == TargetType.FrozenActor) + return IsValidAgainst(target.FrozenActor); + if (target.Type == TargetType.Terrain) { var cell = target.CenterPosition.ToCPos(); diff --git a/OpenRA.Game/Traits/Target.cs b/OpenRA.Game/Traits/Target.cs index 09c177ea13..bc7ae62151 100644 --- a/OpenRA.Game/Traits/Target.cs +++ b/OpenRA.Game/Traits/Target.cs @@ -14,7 +14,7 @@ using System.Linq; namespace OpenRA.Traits { - public enum TargetType { Invalid, Actor, Terrain } + public enum TargetType { Invalid, Actor, Terrain, FrozenActor } public struct Target { public static readonly Target[] None = {}; @@ -22,6 +22,7 @@ namespace OpenRA.Traits TargetType type; Actor actor; + FrozenActor frozen; WPos pos; int generation; @@ -44,8 +45,11 @@ namespace OpenRA.Traits }; } + public static Target FromFrozenActor(FrozenActor a) { return new Target { frozen = a, type = TargetType.FrozenActor }; } + public bool IsValid { get { return Type != TargetType.Invalid; } } public Actor Actor { get { return actor; } } + public FrozenActor FrozenActor { get { return frozen; } } public TargetType Type { @@ -71,10 +75,18 @@ namespace OpenRA.Traits { get { - if (Type == TargetType.Invalid) + switch (Type) + { + case TargetType.Actor: + return actor.CenterPosition; + case TargetType.FrozenActor: + return frozen.CenterPosition; + case TargetType.Terrain: + return pos; + default: + case TargetType.Invalid: throw new InvalidOperationException("Attempting to query the position of an invalid Target"); - - return actor != null ? actor.CenterPosition : pos; + } } } @@ -84,17 +96,23 @@ namespace OpenRA.Traits { get { - if (Type == TargetType.Invalid) + switch (Type) + { + case TargetType.Actor: + var targetable = actor.TraitOrDefault(); + if (targetable == null) + return new [] { actor.CenterPosition }; + + var positions = targetable.TargetablePositions(actor); + return positions.Any() ? positions : new [] { actor.CenterPosition }; + case TargetType.FrozenActor: + return new [] { frozen.CenterPosition }; + case TargetType.Terrain: + return new [] { pos }; + default: + case TargetType.Invalid: return NoPositions; - - if (actor == null) - return new []{pos}; - - var targetable = actor.TraitOrDefault(); - if (targetable == null) - return new []{actor.CenterPosition}; - - return targetable.TargetablePositions(actor); + } } } diff --git a/OpenRA.Game/Traits/Util.cs b/OpenRA.Game/Traits/Util.cs index c793944b5f..5e9119e8da 100755 --- a/OpenRA.Game/Traits/Util.cs +++ b/OpenRA.Game/Traits/Util.cs @@ -134,13 +134,7 @@ namespace OpenRA.Traits public static IEnumerable AdjacentCells(Target target) { - var cells = target.Type == TargetType.Actor - ? target.Actor.OccupiesSpace.OccupiedCells().Select(c => c.First).ToArray() - : new CPos[] { }; - - if (cells.Length == 0) - cells = new CPos[] { target.CenterPosition.ToCPos() }; - + var cells = target.Positions.Select(p => p.ToCPos()).Distinct(); return Util.ExpandFootprint(cells, true); } } diff --git a/OpenRA.Mods.RA/Activities/Attack.cs b/OpenRA.Mods.RA/Activities/Attack.cs index a6ccc10edb..0d8f045cd6 100755 --- a/OpenRA.Mods.RA/Activities/Attack.cs +++ b/OpenRA.Mods.RA/Activities/Attack.cs @@ -53,10 +53,11 @@ namespace OpenRA.Mods.RA.Activities if (IsCanceled) return NextActivity; - if (!Target.IsValid) + var type = Target.Type; + if (type != TargetType.Actor && type != TargetType.Terrain) return NextActivity; - if (!self.Owner.HasFogVisibility() && Target.Actor != null && Target.Actor.HasTrait() && !self.Owner.Shroud.IsTargetable(Target.Actor)) + if (type == TargetType.Actor && !self.Owner.HasFogVisibility() && Target.Actor.HasTrait() && !self.Owner.Shroud.IsTargetable(Target.Actor)) return NextActivity; if (targetable != null && !targetable.TargetableBy(Target.Actor, self)) diff --git a/OpenRA.Mods.RA/Attack/AttackBase.cs b/OpenRA.Mods.RA/Attack/AttackBase.cs index 7406e0be06..009a94d559 100644 --- a/OpenRA.Mods.RA/Attack/AttackBase.cs +++ b/OpenRA.Mods.RA/Attack/AttackBase.cs @@ -216,13 +216,15 @@ namespace OpenRA.Mods.RA public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - if (target.Type == TargetType.Actor) + switch (target.Type) + { + case TargetType.Actor: return CanTargetActor(self, target.Actor, modifiers, ref cursor); - - if (target.Type == TargetType.Terrain) + case TargetType.Terrain: return CanTargetLocation(self, target.CenterPosition.ToCPos(), othersAtTarget, modifiers, ref cursor); - - return false; + default: + return false; + } } public bool IsQueued { get; protected set; } From 3e605b1ee9a684f79cf5e25b493db5d8827a625d Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 8 Aug 2013 18:47:54 +1200 Subject: [PATCH 11/23] Add plumbing for issuing orders against frozen actors. --- OpenRA.Game/Orders/UnitOrderGenerator.cs | 35 +++++++++++++++---- OpenRA.Mods.RA/Captures.cs | 11 +++--- OpenRA.Mods.RA/EngineerRepair.cs | 8 +++-- OpenRA.Mods.RA/Infiltrates.cs | 6 ++++ OpenRA.Mods.RA/LegacyCaptures.cs | 7 +++- .../Orders/EnterBuildingOrderTargeter.cs | 12 ++++--- OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs | 33 ++++++++++------- OpenRA.Mods.RA/RepairsBridges.cs | 8 +++-- OpenRA.Mods.RA/SupplyTruck.cs | 13 ++++--- 9 files changed, 92 insertions(+), 41 deletions(-) diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index 26f1a79ece..d322129ffe 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -24,8 +24,20 @@ namespace OpenRA.Orders .OrderByDescending(a => a.Info.SelectionPriority()) .FirstOrDefault(); + Target target; + if (underCursor != null) + target = Target.FromActor(underCursor); + else + { + var frozen = world.FindFrozenActorsAtMouse(mi.Location) + .Where(a => a.Info.Traits.Contains()) + .OrderByDescending(a => a.Info.SelectionPriority()) + .FirstOrDefault(); + target = frozen != null ? Target.FromFrozenActor(frozen) : Target.FromCell(xy); + } + var orders = world.Selection.Actors - .Select(a => OrderForUnit(a, xy, mi, underCursor)) + .Select(a => OrderForUnit(a, target, mi)) .Where(o => o != null) .ToArray(); @@ -61,8 +73,20 @@ namespace OpenRA.Orders useSelect = true; } + Target target; + if (underCursor != null) + target = Target.FromActor(underCursor); + else + { + var frozen = world.FindFrozenActorsAtMouse(mi.Location) + .Where(a => a.Info.Traits.Contains()) + .OrderByDescending(a => a.Info.SelectionPriority()) + .FirstOrDefault(); + target = frozen != null ? Target.FromFrozenActor(frozen) : Target.FromCell(xy); + } + var orders = world.Selection.Actors - .Select(a => OrderForUnit(a, xy, mi, underCursor)) + .Select(a => OrderForUnit(a, target, mi)) .Where(o => o != null) .ToArray(); @@ -70,12 +94,12 @@ namespace OpenRA.Orders return cursorName ?? (useSelect ? "select" : "default"); } - static UnitOrderResult OrderForUnit(Actor self, CPos xy, MouseInput mi, Actor underCursor) + static UnitOrderResult OrderForUnit(Actor self, Target target, MouseInput mi) { if (self.Owner != self.World.LocalPlayer) return null; - if (self.Destroyed) + if (self.Destroyed || target.Type == TargetType.Invalid) return null; if (mi.Button == Game.mouseButtonPreference.Action) @@ -85,7 +109,7 @@ namespace OpenRA.Orders .Select(x => new { Trait = trait, Order = x } )) .OrderByDescending(x => x.Order.OrderPriority)) { - var actorsAt = self.World.ActorMap.GetUnitsAt(xy).ToList(); + var actorsAt = self.World.ActorMap.GetUnitsAt(target.CenterPosition.ToCPos()).ToList(); var modifiers = TargetModifiers.None; if (mi.Modifiers.HasModifier(Modifiers.Ctrl)) @@ -96,7 +120,6 @@ namespace OpenRA.Orders modifiers |= TargetModifiers.ForceMove; string cursor = null; - var target = underCursor != null ? Target.FromActor(underCursor) : Target.FromCell(xy); if (o.Order.CanTarget(self, target, actorsAt, modifiers, ref cursor)) return new UnitOrderResult(self, o.Order, o.Trait, cursor, target); } diff --git a/OpenRA.Mods.RA/Captures.cs b/OpenRA.Mods.RA/Captures.cs index cc8242fd68..6d7117bc33 100644 --- a/OpenRA.Mods.RA/Captures.cs +++ b/OpenRA.Mods.RA/Captures.cs @@ -98,13 +98,12 @@ namespace OpenRA.Mods.RA { var canTargetActor = useCaptureCursor(target); cursor = canTargetActor ? "ability" : "move-blocked"; + return canTargetActor; + } - if (canTargetActor) - { - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - return true; - } - + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + // TODO: Not yet supported return false; } } diff --git a/OpenRA.Mods.RA/EngineerRepair.cs b/OpenRA.Mods.RA/EngineerRepair.cs index 9d2c6671b3..ab77693cb1 100644 --- a/OpenRA.Mods.RA/EngineerRepair.cs +++ b/OpenRA.Mods.RA/EngineerRepair.cs @@ -65,13 +65,17 @@ namespace OpenRA.Mods.RA if (self.Owner.Stances[target.Owner] != Stance.Ally) return false; - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - if (target.GetDamageState() == DamageState.Undamaged) cursor = "goldwrench-blocked"; return true; } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + // TODO: Not yet supported + return false; + } } } diff --git a/OpenRA.Mods.RA/Infiltrates.cs b/OpenRA.Mods.RA/Infiltrates.cs index d7ae411874..d315cccd9c 100644 --- a/OpenRA.Mods.RA/Infiltrates.cs +++ b/OpenRA.Mods.RA/Infiltrates.cs @@ -103,6 +103,12 @@ namespace OpenRA.Mods.RA return true; } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + // TODO: Not yet supported + return false; + } } } } diff --git a/OpenRA.Mods.RA/LegacyCaptures.cs b/OpenRA.Mods.RA/LegacyCaptures.cs index 5c393fa796..e07b75afeb 100644 --- a/OpenRA.Mods.RA/LegacyCaptures.cs +++ b/OpenRA.Mods.RA/LegacyCaptures.cs @@ -104,13 +104,18 @@ namespace OpenRA.Mods.RA cursor = lowEnoughHealth ? "enter" : "capture"; - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); return true; } cursor = "enter-blocked"; return false; } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + // TODO: Not yet supported + return false; + } } } } diff --git a/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs b/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs index 26f151a64c..f8792a1871 100755 --- a/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs @@ -29,15 +29,17 @@ namespace OpenRA.Mods.RA.Orders public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!target.HasTrait()) - return false; - - if (!canTarget(target)) + if (!target.HasTrait() || !canTarget(target)) return false; cursor = useEnterCursor(target) ? "enter" : "enter-blocked"; - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); return true; } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + // TODO: Not yet supported + return false; + } } } diff --git a/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs b/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs index 52e51121a6..3ed8ae752e 100755 --- a/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs @@ -20,7 +20,7 @@ namespace OpenRA.Mods.RA.Orders readonly string cursor; readonly bool targetEnemyUnits, targetAllyUnits; - public UnitOrderTargeter( string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits ) + public UnitOrderTargeter(string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits) { this.OrderID = order; this.OrderPriority = priority; @@ -34,23 +34,32 @@ namespace OpenRA.Mods.RA.Orders public bool? ForceAttack = null; public abstract bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor); + public abstract bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor); public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - if (target.Type != TargetType.Actor) + var type = target.Type; + if (type != TargetType.Actor && type != TargetType.FrozenActor) return false; cursor = this.cursor; IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - if (ForceAttack != null && modifiers.HasModifier(TargetModifiers.ForceAttack) != ForceAttack) return false; + if (ForceAttack != null && modifiers.HasModifier(TargetModifiers.ForceAttack) != ForceAttack) + return false; - var playerRelationship = self.Owner.Stances[target.Actor.Owner]; + var owner = type == TargetType.FrozenActor ? target.FrozenActor.Owner : target.Actor.Owner; + var playerRelationship = self.Owner.Stances[owner]; - if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Ally && !targetAllyUnits) return false; - if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Enemy && !targetEnemyUnits) return false; + if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Ally && !targetAllyUnits) + return false; - return CanTargetActor(self, target.Actor, modifiers, ref cursor); + if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Enemy && !targetEnemyUnits) + return false; + + return type == TargetType.FrozenActor ? + CanTargetFrozenActor(self, target.FrozenActor, modifiers, ref cursor) : + CanTargetActor(self, target.Actor, modifiers, ref cursor); } public virtual bool IsQueued { get; protected set; } @@ -68,12 +77,12 @@ namespace OpenRA.Mods.RA.Orders public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!target.TraitsImplementing().Any(t => t.TargetTypes.Contains(targetType))) - return false; + return target.TraitsImplementing().Any(t => t.TargetTypes.Contains(targetType)); + } - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - - return true; + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + return target.Info.Traits.WithInterface().Any(t => t.GetTargetTypes().Contains(targetType)); } } } diff --git a/OpenRA.Mods.RA/RepairsBridges.cs b/OpenRA.Mods.RA/RepairsBridges.cs index 27024629b7..b95ba0f8ad 100644 --- a/OpenRA.Mods.RA/RepairsBridges.cs +++ b/OpenRA.Mods.RA/RepairsBridges.cs @@ -80,14 +80,18 @@ namespace OpenRA.Mods.RA if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && damage != DamageState.Dead) return false; - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - // Can't repair an undamaged bridge if (damage == DamageState.Undamaged) cursor = "goldwrench-blocked"; return true; } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + // TODO: Bridges don't yet support FrozenUnderFog. + return false; + } } } } diff --git a/OpenRA.Mods.RA/SupplyTruck.cs b/OpenRA.Mods.RA/SupplyTruck.cs index 7e591a3795..a35c98d448 100644 --- a/OpenRA.Mods.RA/SupplyTruck.cs +++ b/OpenRA.Mods.RA/SupplyTruck.cs @@ -72,14 +72,13 @@ namespace OpenRA.Mods.RA public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (target.AppearsHostileTo(self)) - return false; + return target.HasTrait(); + } - if (!target.HasTrait()) - return false; - - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - return true; + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + // TODO: Not yet supported + return false; } } } From 53287ba2c8b9e01a31d4cdb219dbcd2ccc47138a Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 9 Aug 2013 23:44:12 +1200 Subject: [PATCH 12/23] Add helper for resolving orders against frozen actors. --- OpenRA.Mods.RA/ActorExts.cs | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/OpenRA.Mods.RA/ActorExts.cs b/OpenRA.Mods.RA/ActorExts.cs index 9dedf3412b..9cca5a9595 100644 --- a/OpenRA.Mods.RA/ActorExts.cs +++ b/OpenRA.Mods.RA/ActorExts.cs @@ -9,6 +9,7 @@ #endregion using System; +using System.Drawing; using OpenRA.Traits; namespace OpenRA.Mods.RA @@ -44,6 +45,44 @@ namespace OpenRA.Mods.RA return stance == Stance.Enemy; } + + public static Target ResolveFrozenActorOrder(this Actor self, Order order, Color targetLine) + { + // Not targeting a frozen actor + if (order.ExtraData == 0) + return Target.FromOrder(order); + + // Targeted an actor under the fog + var frozenLayer = self.Owner.PlayerActor.TraitOrDefault(); + if (frozenLayer == null) + return Target.Invalid; + + var frozen = frozenLayer.FromID(order.ExtraData); + if (frozen == null) + return Target.Invalid; + + // Flashes the frozen proxy + self.SetTargetLine(frozen, targetLine, true); + + // Target is still alive - resolve the real order + if (frozen.Actor != null && frozen.Actor.IsInWorld) + return Target.FromActor(frozen.Actor); + + if (!order.Queued) + self.CancelActivity(); + + var move = self.TraitOrDefault(); + if (move != null) + { + // Move within sight range of the frozen actor + var sight = self.TraitOrDefault(); + var range = sight != null ? sight.Range : 2; + + self.QueueActivity(move.MoveWithinRange(Target.FromPos(frozen.CenterPosition), WRange.FromCells(range))); + } + + return Target.Invalid; + } } } From 9c5b526a3d210dc6e1dc887d5be68fba022ca5f2 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 9 Aug 2013 23:44:55 +1200 Subject: [PATCH 13/23] Enable frozen order: Attack. --- OpenRA.Mods.RA/Attack/AttackBase.cs | 41 ++++++++++++++++++----------- OpenRA.Mods.RA/Turreted.cs | 2 +- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/OpenRA.Mods.RA/Attack/AttackBase.cs b/OpenRA.Mods.RA/Attack/AttackBase.cs index 009a94d559..d9ad4fbcc1 100644 --- a/OpenRA.Mods.RA/Attack/AttackBase.cs +++ b/OpenRA.Mods.RA/Attack/AttackBase.cs @@ -55,7 +55,7 @@ namespace OpenRA.Mods.RA return false; if (target.Type == TargetType.Actor && target.Actor.HasTrait() && - !target.Actor.Trait().TargetableBy(target.Actor,self)) + !target.Actor.Trait().TargetableBy(target.Actor, self)) return false; return true; @@ -76,6 +76,7 @@ namespace OpenRA.Mods.RA x.Second(); delayedActions[i] = x; } + delayedActions.RemoveAll(a => a.First <= 0); } @@ -104,8 +105,7 @@ namespace OpenRA.Mods.RA if (Armaments.Count() == 0) yield break; - var negativeDamage = (Armaments.First().Weapon.Warheads[0].Damage < 0); - + var negativeDamage = Armaments.First().Weapon.Warheads[0].Damage < 0; yield return new AttackOrderTargeter("Attack", 6, negativeDamage); } } @@ -114,33 +114,42 @@ namespace OpenRA.Mods.RA { if (order is AttackOrderTargeter) { - if (target.Type == TargetType.Actor) + switch (target.Type) + { + case TargetType.Actor: return new Order("Attack", self, queued) { TargetActor = target.Actor }; - else + case TargetType.FrozenActor: + return new Order("Attack", self, queued) { ExtraData = target.FrozenActor.ID }; + case TargetType.Terrain: return new Order("Attack", self, queued) { TargetLocation = target.CenterPosition.ToCPos() }; + } } + return null; } public virtual void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "Attack" || order.OrderString == "AttackHold") + if (order.OrderString == "Attack") { - var target = Target.FromOrder(order); + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (!target.IsValid) + return; + self.SetTargetLine(target, Color.Red); - AttackTarget(target, order.Queued, order.OrderString == "Attack"); + AttackTarget(target, order.Queued, true); } } public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "Attack" || order.OrderString == "AttackHold") ? "Attack" : null; + return order.OrderString == "Attack" ? "Attack" : null; } public abstract Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove); public bool HasAnyValidWeapons(Target t) { return Armaments.Any(a => a.Weapon.IsValidAgainst(t, self.World)); } - public WRange GetMaximumRange() { return new WRange((int)(1024*Armaments.Max(a => a.Weapon.Range))); } + public WRange GetMaximumRange() { return new WRange((int)(1024 * Armaments.Max(a => a.Weapon.Range))); } public Armament ChooseArmamentForTarget(Target t) { return Armaments.FirstOrDefault(a => a.Weapon.IsValidAgainst(t, self.World)); } @@ -169,16 +178,16 @@ namespace OpenRA.Mods.RA public string OrderID { get; private set; } public int OrderPriority { get; private set; } - bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + bool CanTargetActor(Actor self, Target target, TargetModifiers modifiers, ref string cursor) { IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); cursor = self.Info.Traits.Get().Cursor; - if (self == target) + if (target.Type == TargetType.Actor && target.Actor == self) return false; - if (!self.Trait().HasAnyValidWeapons(Target.FromActor(target))) + if (!self.Trait().HasAnyValidWeapons(target)) return false; if (modifiers.HasModifier(TargetModifiers.ForceAttack)) @@ -189,7 +198,8 @@ namespace OpenRA.Mods.RA var targetableRelationship = negativeDamage ? Stance.Ally : Stance.Enemy; - return self.Owner.Stances[target.Owner] == targetableRelationship; + var owner = target.Type == TargetType.FrozenActor ? target.FrozenActor.Owner : target.Actor.Owner; + return self.Owner.Stances[owner] == targetableRelationship; } bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) @@ -219,7 +229,8 @@ namespace OpenRA.Mods.RA switch (target.Type) { case TargetType.Actor: - return CanTargetActor(self, target.Actor, modifiers, ref cursor); + case TargetType.FrozenActor: + return CanTargetActor(self, target, modifiers, ref cursor); case TargetType.Terrain: return CanTargetLocation(self, target.CenterPosition.ToCPos(), othersAtTarget, modifiers, ref cursor); default: diff --git a/OpenRA.Mods.RA/Turreted.cs b/OpenRA.Mods.RA/Turreted.cs index 68dc809d55..bbd303ae23 100755 --- a/OpenRA.Mods.RA/Turreted.cs +++ b/OpenRA.Mods.RA/Turreted.cs @@ -77,7 +77,7 @@ namespace OpenRA.Mods.RA public virtual void ResolveOrder(Actor self, Order order) { - if (info.AlignWhenIdle && order.OrderString != "Attack" && order.OrderString != "AttackHold") + if (info.AlignWhenIdle && order.OrderString != "Attack") desiredFacing = null; } From d27d5f449e4a01deb08bcc067f4524e967e6e488 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 9 Aug 2013 23:36:41 +1200 Subject: [PATCH 14/23] Enable frozen order: DeliverSupplies. --- OpenRA.Mods.RA/SupplyTruck.cs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/OpenRA.Mods.RA/SupplyTruck.cs b/OpenRA.Mods.RA/SupplyTruck.cs index a35c98d448..1ff07ba7b2 100644 --- a/OpenRA.Mods.RA/SupplyTruck.cs +++ b/OpenRA.Mods.RA/SupplyTruck.cs @@ -10,10 +10,10 @@ using System.Collections.Generic; using System.Drawing; +using OpenRA.FileFormats; using OpenRA.Mods.RA.Activities; using OpenRA.Mods.RA.Buildings; using OpenRA.Mods.RA.Orders; -using OpenRA.FileFormats; using OpenRA.Traits; namespace OpenRA.Mods.RA @@ -28,11 +28,11 @@ namespace OpenRA.Mods.RA class SupplyTruck : IIssueOrder, IResolveOrder, IOrderVoice { - SupplyTruckInfo Info; + SupplyTruckInfo info; public SupplyTruck(SupplyTruckInfo info) { - Info = info; + this.info = info; } public IEnumerable Orders @@ -42,10 +42,13 @@ namespace OpenRA.Mods.RA public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "DeliverSupplies") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "DeliverSupplies") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; } public string VoicePhraseForOrder(Actor self, Order order) @@ -55,12 +58,18 @@ namespace OpenRA.Mods.RA public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "DeliverSupplies") - { - self.SetTargetLine(Target.FromOrder(order), Color.Yellow); + if (order.OrderString != "DeliverSupplies") + return; + + var target = self.ResolveFrozenActorOrder(order, Color.Yellow); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new Enter(order.TargetActor, new DonateSupplies(order.TargetActor, Info.Payload))); - } + + self.SetTargetLine(target, Color.Yellow); + self.QueueActivity(new Enter(target.Actor, new DonateSupplies(target.Actor, info.Payload))); } class SupplyTruckOrderTargeter : UnitOrderTargeter @@ -77,8 +86,7 @@ namespace OpenRA.Mods.RA public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) { - // TODO: Not yet supported - return false; + return target.Info.Traits.Contains(); } } } From f2ecea4731cd35e834c117654c12b0e85103be44 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 10 Aug 2013 12:06:04 +1200 Subject: [PATCH 15/23] Enable frozen order: LegacyCaptureActor. --- .../Activities/LegacyCaptureActor.cs | 23 +++--- OpenRA.Mods.RA/LegacyCapturable.cs | 30 +++---- OpenRA.Mods.RA/LegacyCaptures.cs | 80 +++++++++++-------- 3 files changed, 66 insertions(+), 67 deletions(-) diff --git a/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs b/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs index 8c2fb6d2ea..a2ba106cca 100644 --- a/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs +++ b/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs @@ -9,9 +9,9 @@ #endregion using System.Linq; -using OpenRA.Traits; -using OpenRA.Mods.RA.Move; using OpenRA.Mods.RA.Buildings; +using OpenRA.Mods.RA.Move; +using OpenRA.Traits; namespace OpenRA.Mods.RA.Activities { @@ -26,26 +26,27 @@ namespace OpenRA.Mods.RA.Activities if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; - var b = target.Actor.TraitOrDefault(); + var actor = target.Actor; + var b = actor.TraitOrDefault(); if (b != null && b.Locked) return NextActivity; var capturesInfo = self.Info.Traits.Get(); - var capturableInfo = target.Actor.Info.Traits.Get(); + var capturableInfo = actor.Info.Traits.Get(); - var health = target.Actor.Trait(); + var health = actor.Trait(); self.World.AddFrameEndTask(w => { var lowEnoughHealth = health.HP <= capturableInfo.CaptureThreshold * health.MaxHP; - if (!capturesInfo.Sabotage || lowEnoughHealth || target.Actor.Owner.NonCombatant) + if (!capturesInfo.Sabotage || lowEnoughHealth || actor.Owner.NonCombatant) { - var oldOwner = target.Actor.Owner; + var oldOwner = actor.Owner; - target.Actor.ChangeOwner(self.Owner); + actor.ChangeOwner(self.Owner); - foreach (var t in target.Actor.TraitsImplementing()) - t.OnCapture(target.Actor, self, oldOwner, self.Owner); + foreach (var t in actor.TraitsImplementing()) + t.OnCapture(actor, self, oldOwner, self.Owner); if (b != null && b.Locked) b.Unlock(); @@ -53,7 +54,7 @@ namespace OpenRA.Mods.RA.Activities else { var damage = (int)(health.MaxHP * capturesInfo.SabotageHPRemoval); - target.Actor.InflictDamage(self, damage, null); + actor.InflictDamage(self, damage, null); } self.Destroy(); diff --git a/OpenRA.Mods.RA/LegacyCapturable.cs b/OpenRA.Mods.RA/LegacyCapturable.cs index 639dbddbb4..3375d1d024 100644 --- a/OpenRA.Mods.RA/LegacyCapturable.cs +++ b/OpenRA.Mods.RA/LegacyCapturable.cs @@ -15,7 +15,7 @@ using OpenRA.FileFormats; namespace OpenRA.Mods.RA { [Desc("This actor can be captured by a unit with LegacyCaptures: trait.")] - class LegacyCapturableInfo : ITraitInfo + class LegacyCapturableInfo : TraitInfo { [Desc("Type of actor (the LegacyCaptures: trait defines what Types it can capture).")] public readonly string Type = "building"; @@ -25,40 +25,28 @@ namespace OpenRA.Mods.RA [Desc("Health percentage the target must be at (or below) before it can be captured.")] public readonly float CaptureThreshold = 0.5f; - public object Create(ActorInitializer init) { return new LegacyCapturable(init.self, this); } - } - - class LegacyCapturable - { - [Sync] Actor self; - public LegacyCapturableInfo Info; - - public LegacyCapturable(Actor self, LegacyCapturableInfo info) - { - this.self = self; - Info = info; - } - - public bool CanBeTargetedBy(Actor captor) + public bool CanBeTargetedBy(Actor captor, Player owner) { var c = captor.TraitOrDefault(); if (c == null) return false; - var playerRelationship = self.Owner.Stances[captor.Owner]; - if (playerRelationship == Stance.Ally && !Info.AllowAllies) + var playerRelationship = owner.Stances[captor.Owner]; + if (playerRelationship == Stance.Ally && !AllowAllies) return false; - if (playerRelationship == Stance.Enemy && !Info.AllowEnemies) + if (playerRelationship == Stance.Enemy && !AllowEnemies) return false; - if (playerRelationship == Stance.Neutral && !Info.AllowNeutral) + if (playerRelationship == Stance.Neutral && !AllowNeutral) return false; - if (!c.Info.CaptureTypes.Contains(Info.Type)) + if (!c.Info.CaptureTypes.Contains(Type)) return false; return true; } } + + class LegacyCapturable { } } diff --git a/OpenRA.Mods.RA/LegacyCaptures.cs b/OpenRA.Mods.RA/LegacyCaptures.cs index e07b75afeb..8193145f8a 100644 --- a/OpenRA.Mods.RA/LegacyCaptures.cs +++ b/OpenRA.Mods.RA/LegacyCaptures.cs @@ -12,11 +12,11 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using OpenRA.FileFormats; using OpenRA.Mods.RA.Activities; using OpenRA.Mods.RA.Buildings; using OpenRA.Mods.RA.Orders; using OpenRA.Traits; -using OpenRA.FileFormats; namespace OpenRA.Mods.RA { @@ -36,11 +36,9 @@ namespace OpenRA.Mods.RA class LegacyCaptures : IIssueOrder, IResolveOrder, IOrderVoice { public readonly LegacyCapturesInfo Info; - readonly Actor self; public LegacyCaptures(Actor self, LegacyCapturesInfo info) { - this.self = self; Info = info; } @@ -48,73 +46,85 @@ namespace OpenRA.Mods.RA { get { - yield return new LegacyCaptureOrderTargeter(CanCapture); + yield return new LegacyCaptureOrderTargeter(Info.Sabotage); } } public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "LegacyCaptureActor") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "LegacyCaptureActor") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; } public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "LegacyCaptureActor") ? "Attack" : null; + return order.OrderString == "LegacyCaptureActor" ? "Attack" : null; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "LegacyCaptureActor") - { - self.SetTargetLine(Target.FromOrder(order), Color.Red); + if (order.OrderString != "LegacyCaptureActor") + return; + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new Enter(order.TargetActor, new LegacyCaptureActor(Target.FromOrder(order)))); - } - } - bool CanCapture(Actor target) - { - var c = target.TraitOrDefault(); - return c != null && c.CanBeTargetedBy(self); + self.SetTargetLine(target, Color.Red); + self.QueueActivity(new Enter(target.Actor, new LegacyCaptureActor(target))); } class LegacyCaptureOrderTargeter : UnitOrderTargeter { - readonly Func useCaptureCursor; + readonly bool sabotage; - public LegacyCaptureOrderTargeter(Func useCaptureCursor) + public LegacyCaptureOrderTargeter(bool sabotage) : base("LegacyCaptureActor", 6, "enter", true, true) { - this.useCaptureCursor = useCaptureCursor; + this.sabotage = sabotage; } public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - var canTargetActor = useCaptureCursor(target); - - if (canTargetActor) + var c = target.Info.Traits.GetOrDefault(); + if (c == null || !c.CanBeTargetedBy(self, target.Owner)) { - var c = target.Trait(); - var health = target.Trait(); - var lowEnoughHealth = health.HP <= c.Info.CaptureThreshold * health.MaxHP; - - cursor = lowEnoughHealth ? "enter" : "capture"; - - return true; + cursor = "enter-blocked"; + return false; } - cursor = "enter-blocked"; - return false; + var health = target.Trait(); + var lowEnoughHealth = health.HP <= c.CaptureThreshold * health.MaxHP; + + cursor = !sabotage || lowEnoughHealth || target.Owner.NonCombatant + ? "capture" : "enter"; + return true; } public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) { - // TODO: Not yet supported - return false; + var c = target.Info.Traits.GetOrDefault(); + if (c == null || !c.CanBeTargetedBy(self, target.Owner)) + { + cursor = "enter-blocked"; + return false; + } + + var health = target.Info.Traits.GetOrDefault(); + var lowEnoughHealth = target.HP <= c.CaptureThreshold * health.HP; + + cursor = !sabotage || lowEnoughHealth || target.Owner.NonCombatant + ? "capture" : "enter"; + + return true; } } } From aab63e6f2d5594fcef74a6faf72151229f563c91 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 10 Aug 2013 12:13:58 +1200 Subject: [PATCH 16/23] Enable frozen order: C4. --- OpenRA.Mods.RA/C4Demolition.cs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/OpenRA.Mods.RA/C4Demolition.cs b/OpenRA.Mods.RA/C4Demolition.cs index f200ef8bad..59d876674f 100644 --- a/OpenRA.Mods.RA/C4Demolition.cs +++ b/OpenRA.Mods.RA/C4Demolition.cs @@ -26,11 +26,11 @@ namespace OpenRA.Mods.RA class C4Demolition : IIssueOrder, IResolveOrder, IOrderVoice { - readonly C4DemolitionInfo Info; + readonly C4DemolitionInfo info; public C4Demolition(C4DemolitionInfo info) { - Info = info; + this.info = info; } public IEnumerable Orders @@ -38,28 +38,36 @@ namespace OpenRA.Mods.RA get { yield return new TargetTypeOrderTargeter("C4", "C4", 6, "c4", true, false); } } - 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 == "C4") - return new Order("C4", self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "C4") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "C4") - { - self.SetTargetLine(Target.FromOrder(order), Color.Red); + if (order.OrderString != "C4") + return; + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new Enter(order.TargetActor, new Demolish(order.TargetActor, Info.C4Delay))); - } + + self.SetTargetLine(target, Color.Red); + self.QueueActivity(new Enter(target.Actor, new Demolish(target.Actor, info.C4Delay))); } public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "C4") ? "Attack" : null; + return order.OrderString == "C4" ? "Attack" : null; } } } From 80870dcca8e3797014ce56e23eeee68bce843187 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 10 Aug 2013 12:21:28 +1200 Subject: [PATCH 17/23] Enable frozen order: DemoAttack. --- OpenRA.Mods.RA/DemoTruck.cs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/OpenRA.Mods.RA/DemoTruck.cs b/OpenRA.Mods.RA/DemoTruck.cs index 179fabab55..3ae88887dd 100644 --- a/OpenRA.Mods.RA/DemoTruck.cs +++ b/OpenRA.Mods.RA/DemoTruck.cs @@ -17,8 +17,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - // Exception when overriding Chronoshift event; removed for now, will look into it. - class DemoTruckInfo : TraitInfo, Requires {} + class DemoTruckInfo : TraitInfo, Requires { } class DemoTruck : IIssueOrder, IResolveOrder, IOrderVoice { @@ -41,10 +40,13 @@ namespace OpenRA.Mods.RA public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "DemoAttack" || order.OrderID == "DemoDeploy") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "DemoAttack" && order.OrderID != "DemoDeploy") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; } public string VoicePhraseForOrder(Actor self, Order order) @@ -56,13 +58,18 @@ namespace OpenRA.Mods.RA { if (order.OrderString == "DemoAttack") { - self.SetTargetLine(Target.FromOrder(order), Color.Red); - self.World.AddFrameEndTask(w => - { - self.QueueActivity(new MoveAdjacentTo(Target.FromOrder(order))); - self.QueueActivity(new CallFunc(() => Explode(self))); - }); + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) + self.CancelActivity(); + + self.SetTargetLine(target, Color.Red); + self.QueueActivity(new MoveAdjacentTo(target)); + self.QueueActivity(new CallFunc(() => Explode(self))); } + if (order.OrderString == "DemoDeploy") Explode(self); } From ebd0c210d997eb724930d3465559c6f0de108e6b Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 11 Aug 2013 17:17:39 +1200 Subject: [PATCH 18/23] Enable frozen order: Captures. --- OpenRA.Mods.RA/Capturable.cs | 47 ++++++++++----------- OpenRA.Mods.RA/Captures.cs | 82 +++++++++++++++++++++++------------- 2 files changed, 74 insertions(+), 55 deletions(-) diff --git a/OpenRA.Mods.RA/Capturable.cs b/OpenRA.Mods.RA/Capturable.cs index ffe33564e2..0c3d2c5745 100644 --- a/OpenRA.Mods.RA/Capturable.cs +++ b/OpenRA.Mods.RA/Capturable.cs @@ -28,6 +28,28 @@ namespace OpenRA.Mods.RA [Desc("Seconds it takes to change the owner.", "You might want to add a CapturableBar: trait, too.")] public readonly int CaptureCompleteTime = 15; + public bool CanBeTargetedBy(Actor captor, Player owner) + { + var c = captor.TraitOrDefault(); + if (c == null) + return false; + + var playerRelationship = owner.Stances[captor.Owner]; + if (playerRelationship == Stance.Ally && !AllowAllies) + return false; + + if (playerRelationship == Stance.Enemy && !AllowEnemies) + return false; + + if (playerRelationship == Stance.Neutral && !AllowNeutral) + return false; + + if (!c.Info.CaptureTypes.Contains(Type)) + return false; + + return true; + } + public object Create(ActorInitializer init) { return new Capturable(init.self, this); } } @@ -45,31 +67,6 @@ namespace OpenRA.Mods.RA Info = info; } - public bool CanBeTargetedBy(Actor captor) - { - var c = captor.TraitOrDefault(); - if (c == null) - return false; - - var playerRelationship = self.Owner.Stances[captor.Owner]; - if (playerRelationship == Stance.Ally && !Info.AllowAllies) - return false; - - if (playerRelationship == Stance.Enemy && !Info.AllowEnemies) - return false; - - if (playerRelationship == Stance.Neutral && !Info.AllowNeutral) - return false; - - if (!c.Info.CaptureTypes.Contains(Info.Type)) - return false; - - if (CaptureInProgress) - return false; - - return true; - } - public void BeginCapture(Actor captor) { var building = self.TraitOrDefault(); diff --git a/OpenRA.Mods.RA/Captures.cs b/OpenRA.Mods.RA/Captures.cs index 6d7117bc33..72c6cdc8c2 100644 --- a/OpenRA.Mods.RA/Captures.cs +++ b/OpenRA.Mods.RA/Captures.cs @@ -12,11 +12,11 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using OpenRA.FileFormats; using OpenRA.Mods.RA.Activities; using OpenRA.Mods.RA.Buildings; using OpenRA.Mods.RA.Orders; using OpenRA.Traits; -using OpenRA.FileFormats; namespace OpenRA.Mods.RA { @@ -34,11 +34,9 @@ namespace OpenRA.Mods.RA class Captures : IIssueOrder, IResolveOrder, IOrderVoice { public readonly CapturesInfo Info; - readonly Actor self; public Captures(Actor self, CapturesInfo info) { - this.self = self; Info = info; } @@ -46,65 +44,89 @@ namespace OpenRA.Mods.RA { get { - yield return new CaptureOrderTargeter(CanCapture); + yield return new CaptureOrderTargeter(); } } public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "CaptureActor") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "CaptureActor") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + } + + bool IsValidOrder(Actor self, Order order) + { + // Not targeting an actor + if (order.ExtraData == 0 && order.TargetActor == null) + return false; + + if (order.ExtraData != 0) + { + // Targeted an actor under the fog + var frozenLayer = self.Owner.PlayerActor.TraitOrDefault(); + if (frozenLayer == null) + return false; + + var frozen = frozenLayer.FromID(order.ExtraData); + if (frozen == null) + return false; + + var ci = frozen.Info.Traits.GetOrDefault(); + return ci != null && ci.CanBeTargetedBy(self, frozen.Owner); + } + + var c = order.TargetActor.TraitOrDefault(); + return c != null && !c.CaptureInProgress && c.Info.CanBeTargetedBy(self, order.TargetActor.Owner); } public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "CaptureActor" && CanCapture(order.TargetActor)) ? "Attack" : null; + return order.OrderString == "CaptureActor" && IsValidOrder(self, order) + ? "Attack" : null; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "CaptureActor") - { - if (!CanCapture(order.TargetActor)) - return; + if (order.OrderString != "CaptureActor" || !IsValidOrder(self, order)) + return; - self.SetTargetLine(Target.FromOrder(order), Color.Red); + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new CaptureActor(Target.FromOrder(order))); - } - } - bool CanCapture(Actor target) - { - var c = target.TraitOrDefault(); - return c != null && c.CanBeTargetedBy(self); + self.SetTargetLine(target, Color.Red); + self.QueueActivity(new CaptureActor(target)); } } class CaptureOrderTargeter : UnitOrderTargeter { - readonly Func useCaptureCursor; - - public CaptureOrderTargeter(Func useCaptureCursor) - : base("CaptureActor", 6, "enter", true, true) - { - this.useCaptureCursor = useCaptureCursor; - } + public CaptureOrderTargeter() : base("CaptureActor", 6, "enter", true, true) { } public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - var canTargetActor = useCaptureCursor(target); + var c = target.TraitOrDefault(); + + var canTargetActor = c != null && !c.CaptureInProgress && c.Info.CanBeTargetedBy(self, target.Owner); cursor = canTargetActor ? "ability" : "move-blocked"; return canTargetActor; } public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) { - // TODO: Not yet supported - return false; + var c = target.Info.Traits.GetOrDefault(); + + var canTargetActor = c != null && c.CanBeTargetedBy(self, target.Owner); + cursor = canTargetActor ? "ability" : "move-blocked"; + return canTargetActor; } } } From 2aed55f5f6f4a5dc98792de46320ece8a8ec2fc9 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 11 Aug 2013 20:47:18 +1200 Subject: [PATCH 19/23] Enable frozen order: EngineerRepair. --- OpenRA.Mods.RA/EngineerRepair.cs | 64 +++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/OpenRA.Mods.RA/EngineerRepair.cs b/OpenRA.Mods.RA/EngineerRepair.cs index ab77693cb1..86b7bc2154 100644 --- a/OpenRA.Mods.RA/EngineerRepair.cs +++ b/OpenRA.Mods.RA/EngineerRepair.cs @@ -28,28 +28,58 @@ namespace OpenRA.Mods.RA public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "EngineerRepair") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "EngineerRepair") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + } + + bool IsValidOrder(Actor self, Order order) + { + // Not targeting a frozen actor + if (order.ExtraData == 0 && order.TargetActor == null) + return false; + + if (order.ExtraData != 0) + { + // Targeted an actor under the fog + var frozenLayer = self.Owner.PlayerActor.TraitOrDefault(); + if (frozenLayer == null) + return false; + + var frozen = frozenLayer.FromID(order.ExtraData); + if (frozen == null) + return false; + + return frozen.DamageState > DamageState.Undamaged; + } + + return order.TargetActor.GetDamageState() > DamageState.Undamaged; } public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "EngineerRepair" && - order.TargetActor.GetDamageState() > DamageState.Undamaged) ? "Attack" : null; + return order.OrderString == "EngineerRepair" && IsValidOrder(self, order) + ? "Attack" : null; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "EngineerRepair" && - order.TargetActor.GetDamageState() > DamageState.Undamaged) - { - self.SetTargetLine(Target.FromOrder(order), Color.Yellow); + if (order.OrderString != "EngineerRepair" || !IsValidOrder(self, order)) + return; + var target = self.ResolveFrozenActorOrder(order, Color.Yellow); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new Enter(order.TargetActor, new RepairBuilding(order.TargetActor))); - } + + self.SetTargetLine(target, Color.Yellow); + self.QueueActivity(new Enter(target.Actor, new RepairBuilding(target.Actor))); } class EngineerRepairOrderTargeter : UnitOrderTargeter @@ -73,8 +103,16 @@ namespace OpenRA.Mods.RA public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) { - // TODO: Not yet supported - return false; + if (!target.Info.Traits.Contains()) + return false; + + if (self.Owner.Stances[target.Owner] != Stance.Ally) + return false; + + if (target.DamageState == DamageState.Undamaged) + cursor = "goldwrench-blocked"; + + return true; } } } From a88b5e98d797e04117edd74ef0682c13fd41dcb8 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 11 Aug 2013 21:49:40 +1200 Subject: [PATCH 20/23] Enable frozen order: Infiltrates. --- OpenRA.Mods.RA/Infiltrates.cs | 92 +++++++++++++++-------- OpenRA.Mods.RA/Missions/Allies04Script.cs | 2 +- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/OpenRA.Mods.RA/Infiltrates.cs b/OpenRA.Mods.RA/Infiltrates.cs index d315cccd9c..00d6a457ab 100644 --- a/OpenRA.Mods.RA/Infiltrates.cs +++ b/OpenRA.Mods.RA/Infiltrates.cs @@ -41,64 +41,84 @@ namespace OpenRA.Mods.RA Info = info; } - public IEnumerable Orders - { - get - { - yield return new InfiltratorOrderTargeter(CanInfiltrate); - } - } + public IEnumerable Orders { get { yield return new InfiltratorOrderTargeter(Info.Types); } } public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "Infiltrate") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "Infiltrate") + return null; + + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; - return null; + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; } + bool IsValidOrder(Actor self, Order order) + { + // Not targeting an actor + if (order.ExtraData == 0 && order.TargetActor == null) + return false; + + if (order.ExtraData != 0) + { + // Targeted an actor under the fog + var frozenLayer = self.Owner.PlayerActor.TraitOrDefault(); + if (frozenLayer == null) + return false; + + var frozen = frozenLayer.FromID(order.ExtraData); + if (frozen == null) + return false; + + var ii = frozen.Info.Traits.GetOrDefault(); + return ii != null && Info.Types.Contains(ii.Type); + } + + var i = order.TargetActor.Info.Traits.GetOrDefault(); + return i != null && Info.Types.Contains(i.Type); + } + public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "Infiltrate" && CanInfiltrate(order.TargetActor)) ? "Attack" : null; + return order.OrderString == "Infiltrate" && IsValidOrder(self, order) + ? "Attack" : null; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "Infiltrate") - { - if (!CanInfiltrate(order.TargetActor)) - return; + if (order.OrderString != "Infiltrate" || !IsValidOrder(self, order)) + return; - self.SetTargetLine(Target.FromOrder(order), Color.Red); - + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new Enter(order.TargetActor, new Infiltrate(order.TargetActor))); - } - } - - bool CanInfiltrate(Actor target) - { - var infiltratable = target.Info.Traits.GetOrDefault(); - return infiltratable != null && Info.Types.Contains(infiltratable.Type); + + self.SetTargetLine(target, Color.Red); + self.QueueActivity(new Enter(target.Actor, new Infiltrate(target.Actor))); } class InfiltratorOrderTargeter : UnitOrderTargeter { - readonly Func useEnterCursor; - - public InfiltratorOrderTargeter(Func useEnterCursor) + string[] infiltrationTypes; + + public InfiltratorOrderTargeter(string[] infiltrationTypes) : base("Infiltrate", 7, "enter", true, false) { ForceAttack = false; - this.useEnterCursor = useEnterCursor; + this.infiltrationTypes = infiltrationTypes; } public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!target.HasTrait()) + var info = target.Info.Traits.GetOrDefault(); + if (info == null) return false; - if (!useEnterCursor(target)) + if (!infiltrationTypes.Contains(info.Type)) cursor = "enter-blocked"; return true; @@ -106,8 +126,14 @@ namespace OpenRA.Mods.RA public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) { - // TODO: Not yet supported - return false; + var info = target.Info.Traits.GetOrDefault(); + if (info == null) + return false; + + if (!infiltrationTypes.Contains(info.Type)) + cursor = "enter-blocked"; + + return true; } } } diff --git a/OpenRA.Mods.RA/Missions/Allies04Script.cs b/OpenRA.Mods.RA/Missions/Allies04Script.cs index e66a5335fd..173d461f2a 100644 --- a/OpenRA.Mods.RA/Missions/Allies04Script.cs +++ b/OpenRA.Mods.RA/Missions/Allies04Script.cs @@ -457,7 +457,7 @@ namespace OpenRA.Mods.RA.Missions } } - class Allies04HijackableInfo : ITraitInfo + class Allies04HijackableInfo : ITraitInfo, Requires { public object Create(ActorInitializer init) { return new Allies04Hijackable(init.self); } } From 587fac377eac877a8119c23ae3f7344a15743647 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 11 Aug 2013 20:17:46 +1200 Subject: [PATCH 21/23] Make the non-support of frozen actor targeting in EnterOrderTargeter explicit. --- OpenRA.Mods.RA/Air/Aircraft.cs | 2 +- OpenRA.Mods.RA/Harvester.cs | 3 ++- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 2 +- ...ildingOrderTargeter.cs => EnterAlliedActorTargeter.cs} | 8 ++++---- OpenRA.Mods.RA/Passenger.cs | 4 ++-- OpenRA.Mods.RA/Repairable.cs | 3 ++- OpenRA.Mods.RA/RepairableNear.cs | 4 ++-- 7 files changed, 14 insertions(+), 12 deletions(-) rename OpenRA.Mods.RA/Orders/{EnterBuildingOrderTargeter.cs => EnterAlliedActorTargeter.cs} (82%) diff --git a/OpenRA.Mods.RA/Air/Aircraft.cs b/OpenRA.Mods.RA/Air/Aircraft.cs index 9240cb9b3e..90e4285b56 100755 --- a/OpenRA.Mods.RA/Air/Aircraft.cs +++ b/OpenRA.Mods.RA/Air/Aircraft.cs @@ -166,7 +166,7 @@ namespace OpenRA.Mods.RA.Air { get { - yield return new EnterOrderTargeter("Enter", 5, false, true, + yield return new EnterAlliedActorTargeter("Enter", 5, target => AircraftCanEnter(target), target => !Reservable.IsReserved(target)); yield return new AircraftMoveOrderTargeter(); diff --git a/OpenRA.Mods.RA/Harvester.cs b/OpenRA.Mods.RA/Harvester.cs index a2126cda1d..692d3bfe81 100644 --- a/OpenRA.Mods.RA/Harvester.cs +++ b/OpenRA.Mods.RA/Harvester.cs @@ -239,7 +239,8 @@ namespace OpenRA.Mods.RA { get { - yield return new EnterOrderTargeter("Deliver", 5, false, true, _ => true, proc => !IsEmpty && proc.Trait().AllowDocking); + yield return new EnterAlliedActorTargeter("Deliver", 5, _ => true, + proc => !IsEmpty && proc.Trait().AllowDocking); yield return new HarvestOrderTargeter(); } } diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 3bf6aed2f6..6f78829aae 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -282,7 +282,6 @@ - @@ -465,6 +464,7 @@ + diff --git a/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs b/OpenRA.Mods.RA/Orders/EnterAlliedActorTargeter.cs similarity index 82% rename from OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs rename to OpenRA.Mods.RA/Orders/EnterAlliedActorTargeter.cs index f8792a1871..6d0b77f49e 100755 --- a/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/EnterAlliedActorTargeter.cs @@ -14,14 +14,14 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA.Orders { - public class EnterOrderTargeter : UnitOrderTargeter + public class EnterAlliedActorTargeter : UnitOrderTargeter { readonly Func canTarget; readonly Func useEnterCursor; - public EnterOrderTargeter(string order, int priority, bool targetEnemy, bool targetAlly, + public EnterAlliedActorTargeter(string order, int priority, Func canTarget, Func useEnterCursor) - : base (order, priority, "enter", targetEnemy, targetAlly) + : base (order, priority, "enter", false, true) { this.canTarget = canTarget; this.useEnterCursor = useEnterCursor; @@ -38,7 +38,7 @@ namespace OpenRA.Mods.RA.Orders public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) { - // TODO: Not yet supported + // Allied actors are never frozen return false; } } diff --git a/OpenRA.Mods.RA/Passenger.cs b/OpenRA.Mods.RA/Passenger.cs index 7a4c9fc6b8..b9e06883fe 100644 --- a/OpenRA.Mods.RA/Passenger.cs +++ b/OpenRA.Mods.RA/Passenger.cs @@ -35,8 +35,8 @@ namespace OpenRA.Mods.RA { get { - yield return new EnterOrderTargeter( "EnterTransport", 6, false, true, - target => IsCorrectCargoType( target ), target => CanEnter( target ) ); + yield return new EnterAlliedActorTargeter("EnterTransport", 6, + target => IsCorrectCargoType(target), target => CanEnter(target)); } } diff --git a/OpenRA.Mods.RA/Repairable.cs b/OpenRA.Mods.RA/Repairable.cs index 35b1e8bc9c..1fb9c2b5a9 100644 --- a/OpenRA.Mods.RA/Repairable.cs +++ b/OpenRA.Mods.RA/Repairable.cs @@ -38,7 +38,8 @@ namespace OpenRA.Mods.RA public IEnumerable Orders { - get { yield return new EnterOrderTargeter( "Repair", 5, false, true, target => CanRepairAt( target ), _ => CanRepair() ); } + get { yield return new EnterAlliedActorTargeter("Repair", 5, + target => CanRepairAt(target), _ => CanRepair()); } } public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued ) diff --git a/OpenRA.Mods.RA/RepairableNear.cs b/OpenRA.Mods.RA/RepairableNear.cs index 132d001278..e8e209f71a 100644 --- a/OpenRA.Mods.RA/RepairableNear.cs +++ b/OpenRA.Mods.RA/RepairableNear.cs @@ -38,8 +38,8 @@ namespace OpenRA.Mods.RA { get { - yield return new EnterOrderTargeter( "RepairNear", 5, false, true, - target => CanRepairAt( target ), _ => ShouldRepair() ); + yield return new EnterAlliedActorTargeter("RepairNear", 5, + target => CanRepairAt(target), _ => ShouldRepair()); } } From 63eb1b8a031fe1726021691c55b3a7d0d6d8547a Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 12 Aug 2013 10:09:38 +1200 Subject: [PATCH 22/23] [Sync] the AutoTargeted actor ID for desync hunting. --- OpenRA.Mods.RA/AutoTarget.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/OpenRA.Mods.RA/AutoTarget.cs b/OpenRA.Mods.RA/AutoTarget.cs index 19f06ada6a..096565c330 100644 --- a/OpenRA.Mods.RA/AutoTarget.cs +++ b/OpenRA.Mods.RA/AutoTarget.cs @@ -43,7 +43,8 @@ namespace OpenRA.Mods.RA public UnitStance stance; [Sync] public int stanceNumber { get { return (int)stance; } } public UnitStance predictedStance; /* NOT SYNCED: do not refer to this anywhere other than UI code */ - [Sync] public int AggressorID; + [Sync] public Actor Aggressor; + [Sync] public Actor TargetedActor; public AutoTarget(Actor self, AutoTargetInfo info) { @@ -75,7 +76,7 @@ namespace OpenRA.Mods.RA if (e.Damage < 0) return; // don't retaliate against healers - AggressorID = (int)e.Attacker.ActorID; + Aggressor = e.Attacker; attack.AttackTarget(Target.FromActor(e.Attacker), false, Info.AllowMovement && stance != UnitStance.Defend); } @@ -87,6 +88,8 @@ namespace OpenRA.Mods.RA var target = ScanForTarget(self, null); if (target != null) { + TargetedActor = target; + var t = Target.FromActor(target); self.SetTargetLine(t, Color.Red, false); attack.AttackTarget(t, false, Info.AllowMovement && stance != UnitStance.Defend); @@ -113,7 +116,10 @@ namespace OpenRA.Mods.RA { var targetActor = ScanForTarget(self, null); if (targetActor != null) + { + TargetedActor = targetActor; attack.AttackTarget(Target.FromActor(targetActor), false, Info.AllowMovement && stance != UnitStance.Defend); + } } Actor ChooseTarget(Actor self, WRange range) From 1040d7f3ef12ae36e2957579d43c1ef14bf1b531 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 12 Aug 2013 20:44:12 +1200 Subject: [PATCH 23/23] Add no-sync check around TickRender. --- OpenRA.Game/Game.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 4693533a10..36d6723990 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -191,7 +191,7 @@ namespace OpenRA if (orderManager.NetFrameNumber == 0) orderManager.LastTickTime = Environment.TickCount; - world.TickRender(worldRenderer); + Sync.CheckSyncUnchanged(world, () => world.TickRender(worldRenderer)); viewport.Tick(); } }