From ebe37a44adcda4e90a0cb4409c05874f3abcc762 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 7 Jun 2019 23:08:11 +0000 Subject: [PATCH] Require force move for all undeploy-triggering orders. --- .../Orders/AircraftMoveOrderTargeter.cs | 57 ---------------- .../Orders/EnterAlliedActorTargeter.cs | 6 +- OpenRA.Mods.Common/Traits/Air/Aircraft.cs | 65 ++++++++++++++++++- .../Buildings/TransformsIntoAircraft.cs | 11 ++++ .../Buildings/TransformsIntoEntersTunnels.cs | 5 +- .../Buildings/TransformsIntoPassenger.cs | 11 ++++ .../Buildings/TransformsIntoRepairable.cs | 11 ++++ OpenRA.Mods.Common/Traits/Carryall.cs | 18 ++--- OpenRA.Mods.Common/Traits/EntersTunnels.cs | 30 +++++++-- OpenRA.Mods.Common/Traits/Harvester.cs | 2 +- OpenRA.Mods.Common/Traits/Passenger.cs | 31 ++++++++- OpenRA.Mods.Common/Traits/Repairable.cs | 30 +++++++-- OpenRA.Mods.Common/Traits/RepairableNear.cs | 31 ++++++++- mods/cnc/rules/structures.yaml | 2 + mods/ra/rules/structures.yaml | 2 + mods/ts/rules/gdi-vehicles.yaml | 6 ++ mods/ts/rules/nod-vehicles.yaml | 12 ++++ mods/ts/rules/shared-structures.yaml | 3 + mods/ts/rules/shared-vehicles.yaml | 6 ++ 19 files changed, 249 insertions(+), 90 deletions(-) delete mode 100644 OpenRA.Mods.Common/Orders/AircraftMoveOrderTargeter.cs diff --git a/OpenRA.Mods.Common/Orders/AircraftMoveOrderTargeter.cs b/OpenRA.Mods.Common/Orders/AircraftMoveOrderTargeter.cs deleted file mode 100644 index b491b9d3ea..0000000000 --- a/OpenRA.Mods.Common/Orders/AircraftMoveOrderTargeter.cs +++ /dev/null @@ -1,57 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2019 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, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using System.Collections.Generic; -using OpenRA.Mods.Common.Traits; -using OpenRA.Traits; - -namespace OpenRA.Mods.Common.Orders -{ - public class AircraftMoveOrderTargeter : IOrderTargeter - { - public string OrderID { get; protected set; } - public int OrderPriority { get; protected set; } - public bool TargetOverridesSelection(TargetModifiers modifiers) - { - return modifiers.HasModifier(TargetModifiers.ForceMove); - } - - readonly AircraftInfo info; - - public AircraftMoveOrderTargeter(AircraftInfo info) - { - this.info = info; - OrderID = "Move"; - OrderPriority = 4; - } - - public virtual bool CanTarget(Actor self, Target target, List othersAtTarget, ref TargetModifiers modifiers, ref string cursor) - { - if (target.Type != TargetType.Terrain) - return false; - - var location = self.World.Map.CellContaining(target.CenterPosition); - var explored = self.Owner.Shroud.IsExplored(location); - cursor = self.World.Map.Contains(location) ? - (self.World.Map.GetTerrainInfo(location).CustomCursor ?? "move") : - "move-blocked"; - - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - - if (!explored && !info.MoveIntoShroud) - cursor = "move-blocked"; - - return true; - } - - public bool IsQueued { get; protected set; } - } -} diff --git a/OpenRA.Mods.Common/Orders/EnterAlliedActorTargeter.cs b/OpenRA.Mods.Common/Orders/EnterAlliedActorTargeter.cs index 4833287b09..d5c4b30494 100644 --- a/OpenRA.Mods.Common/Orders/EnterAlliedActorTargeter.cs +++ b/OpenRA.Mods.Common/Orders/EnterAlliedActorTargeter.cs @@ -16,11 +16,11 @@ namespace OpenRA.Mods.Common.Orders { public class EnterAlliedActorTargeter : UnitOrderTargeter where T : ITraitInfo { - readonly Func canTarget; + readonly Func canTarget; readonly Func useEnterCursor; public EnterAlliedActorTargeter(string order, int priority, - Func canTarget, Func useEnterCursor) + Func canTarget, Func useEnterCursor) : base(order, priority, "enter", false, true) { this.canTarget = canTarget; @@ -29,7 +29,7 @@ namespace OpenRA.Mods.Common.Orders public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!self.Owner.IsAlliedWith(target.Owner) || !target.Info.HasTraitInfo() || !canTarget(target)) + if (!self.Owner.IsAlliedWith(target.Owner) || !target.Info.HasTraitInfo() || !canTarget(target, modifiers)) return false; cursor = useEnterCursor(target) ? "enter" : "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index 5f7b319fa3..de11416cd7 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -22,7 +22,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { public class AircraftInfo : ITraitInfo, IPositionableInfo, IFacingInfo, IMoveInfo, ICruiseAltitudeInfo, - IActorPreviewInitInfo, IEditorActorOptions + IActorPreviewInitInfo, IEditorActorOptions, IObservesVariablesInfo { public readonly WDist CruiseAltitude = new WDist(1280); @@ -120,6 +120,10 @@ namespace OpenRA.Mods.Common.Traits [Desc("Display order for the facing slider in the map editor")] public readonly int EditorFacingDisplayOrder = 3; + [ConsumedConditionReference] + [Desc("Boolean expression defining the condition under which the regular (non-force) move cursor is disabled.")] + public readonly BooleanExpression RequireForceMoveCondition = null; + public int GetInitialFacing() { return InitialFacing; } public WDist GetCruiseAltitude() { return CruiseAltitude; } @@ -197,6 +201,7 @@ namespace OpenRA.Mods.Common.Traits public bool MayYieldReservation { get; private set; } public bool ForceLanding { get; private set; } CPos? landingCell; + bool requireForceMove; public WDist LandAltitude { get; private set; } @@ -245,6 +250,9 @@ namespace OpenRA.Mods.Common.Traits { if (Info.LandOnCondition != null) yield return new VariableObserver(ForceLandConditionChanged, Info.LandOnCondition.Variables); + + if (Info.RequireForceMoveCondition != null) + yield return new VariableObserver(RequireForceMoveConditionChanged, Info.RequireForceMoveCondition.Variables); } void ForceLandConditionChanged(Actor self, IReadOnlyDictionary variables) @@ -252,6 +260,11 @@ namespace OpenRA.Mods.Common.Traits landNow = Info.LandOnCondition.Evaluate(variables); } + void RequireForceMoveConditionChanged(Actor self, IReadOnlyDictionary conditions) + { + requireForceMove = Info.RequireForceMoveCondition.Evaluate(conditions); + } + void INotifyCreated.Created(Actor self) { Created(self); @@ -508,6 +521,14 @@ namespace OpenRA.Mods.Common.Traits self.QueueActivity(new TakeOff(self)); } + bool AircraftCanEnter(Actor a, TargetModifiers modifiers) + { + if (requireForceMove && !modifiers.HasModifier(TargetModifiers.ForceMove)) + return false; + + return AircraftCanEnter(a); + } + public bool AircraftCanEnter(Actor a) { if (self.AppearsHostileTo(a)) @@ -854,9 +875,9 @@ namespace OpenRA.Mods.Common.Traits get { yield return new EnterAlliedActorTargeter("Enter", 5, - target => AircraftCanEnter(target), target => Reservable.IsAvailableFor(target, self)); + AircraftCanEnter, target => Reservable.IsAvailableFor(target, self)); - yield return new AircraftMoveOrderTargeter(Info); + yield return new AircraftMoveOrderTargeter(this); } } @@ -1038,5 +1059,43 @@ namespace OpenRA.Mods.Common.Traits if (!inits.Contains() && !inits.Contains()) inits.Add(new DynamicFacingInit(() => Facing)); } + + public class AircraftMoveOrderTargeter : IOrderTargeter + { + readonly Aircraft aircraft; + + public string OrderID { get { return "Move"; } } + public int OrderPriority { get { return 4; } } + public bool IsQueued { get; protected set; } + + public AircraftMoveOrderTargeter(Aircraft aircraft) + { + this.aircraft = aircraft; + } + + public bool TargetOverridesSelection(TargetModifiers modifiers) + { + return modifiers.HasModifier(TargetModifiers.ForceMove); + } + + public virtual bool CanTarget(Actor self, Target target, List othersAtTarget, ref TargetModifiers modifiers, ref string cursor) + { + if (target.Type != TargetType.Terrain || (aircraft.requireForceMove && !modifiers.HasModifier(TargetModifiers.ForceMove))) + return false; + + var location = self.World.Map.CellContaining(target.CenterPosition); + var explored = self.Owner.Shroud.IsExplored(location); + cursor = self.World.Map.Contains(location) ? + (self.World.Map.GetTerrainInfo(location).CustomCursor ?? "move") : + "move-blocked"; + + IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); + + if (!explored && !aircraft.Info.MoveIntoShroud) + cursor = "move-blocked"; + + return true; + } + } } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs index 2f1529ca30..d1a247662d 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs @@ -33,6 +33,9 @@ namespace OpenRA.Mods.Common.Traits [VoiceReference] public readonly string Voice = "Action"; + [Desc("Require the force-move modifier to display the move cursor.")] + public readonly bool RequiresForceMove = false; + public override object Create(ActorInitializer init) { return new TransformsIntoAircraft(init, this); } } @@ -66,6 +69,14 @@ namespace OpenRA.Mods.Common.Traits } } + public bool AircraftCanEnter(Actor a, TargetModifiers modifiers) + { + if (Info.RequiresForceMove && !modifiers.HasModifier(TargetModifiers.ForceMove)) + return false; + + return AircraftCanEnter(a); + } + public bool AircraftCanEnter(Actor a) { return !self.AppearsHostileTo(a) && Info.DockActors.Contains(a.Info.Name); diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoEntersTunnels.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoEntersTunnels.cs index 562fbab691..b1155b05ba 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoEntersTunnels.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoEntersTunnels.cs @@ -29,6 +29,9 @@ namespace OpenRA.Mods.Common.Traits [VoiceReference] public readonly string Voice = "Action"; + [Desc("Require the force-move modifier to display the enter cursor.")] + public readonly bool RequiresForceMove = false; + public override object Create(ActorInitializer init) { return new TransformsIntoEntersTunnels(this); } } @@ -50,7 +53,7 @@ namespace OpenRA.Mods.Common.Traits get { if (!IsTraitDisabled) - yield return new EntersTunnels.EnterTunnelOrderTargeter(Info.EnterCursor, Info.EnterBlockedCursor); + yield return new EntersTunnels.EnterTunnelOrderTargeter(Info.EnterCursor, Info.EnterBlockedCursor, () => Info.RequiresForceMove); } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoPassenger.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoPassenger.cs index f9bfce1477..593c575ce3 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoPassenger.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoPassenger.cs @@ -29,6 +29,9 @@ namespace OpenRA.Mods.Common.Traits [VoiceReference] public readonly string Voice = "Action"; + [Desc("Require the force-move modifier to display the enter cursor.")] + public readonly bool RequiresForceMove = false; + public override object Create(ActorInitializer init) { return new TransformsIntoPassenger(this); } } @@ -62,6 +65,14 @@ namespace OpenRA.Mods.Common.Traits return null; } + bool IsCorrectCargoType(Actor target, TargetModifiers modifiers) + { + if (Info.RequiresForceMove && !modifiers.HasModifier(TargetModifiers.ForceMove)) + return false; + + return IsCorrectCargoType(target); + } + bool IsCorrectCargoType(Actor target) { var ci = target.Info.TraitInfo(); diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs index 1294cce065..43f8cb01a7 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs @@ -30,6 +30,9 @@ namespace OpenRA.Mods.Common.Traits [VoiceReference] public readonly string Voice = "Action"; + [Desc("Require the force-move modifier to display the enter cursor.")] + public readonly bool RequiresForceMove = false; + public override object Create(ActorInitializer init) { return new TransformsIntoRepairable(this); } } @@ -62,6 +65,14 @@ namespace OpenRA.Mods.Common.Traits return health.DamageState > DamageState.Undamaged && transforms.Any(t => !t.IsTraitDisabled && !t.IsTraitPaused); } + bool CanRepairAt(Actor target, TargetModifiers modifiers) + { + if (Info.RequiresForceMove && !modifiers.HasModifier(TargetModifiers.ForceMove)) + return false; + + return CanRepairAt(target); + } + bool CanRepairAt(Actor target) { return Info.RepairActors.Contains(target.Info.Name); diff --git a/OpenRA.Mods.Common/Traits/Carryall.cs b/OpenRA.Mods.Common/Traits/Carryall.cs index 9345029028..45d82648df 100644 --- a/OpenRA.Mods.Common/Traits/Carryall.cs +++ b/OpenRA.Mods.Common/Traits/Carryall.cs @@ -257,7 +257,7 @@ namespace OpenRA.Mods.Common.Traits yield return new CarryallPickupOrderTargeter(); yield return new DeployOrderTargeter("Unload", 10, () => CanUnload() ? Info.UnloadCursor : Info.UnloadBlockedCursor); - yield return new CarryallDeliverUnitTargeter(aircraftInfo, Info, CarryableOffset); + yield return new CarryallDeliverUnitTargeter(aircraftInfo, Info); } } @@ -355,23 +355,23 @@ namespace OpenRA.Mods.Common.Traits } } - class CarryallDeliverUnitTargeter : AircraftMoveOrderTargeter + class CarryallDeliverUnitTargeter : IOrderTargeter { readonly AircraftInfo aircraftInfo; readonly CarryallInfo info; - readonly WVec carryableOffset; - public CarryallDeliverUnitTargeter(AircraftInfo aircraftInfo, CarryallInfo info, WVec carryableOffset) - : base(aircraftInfo) + public string OrderID { get { return "DeliverUnit"; } } + public int OrderPriority { get { return 6; } } + public bool IsQueued { get; protected set; } + public bool TargetOverridesSelection(TargetModifiers modifiers) { return true; } + + public CarryallDeliverUnitTargeter(AircraftInfo aircraftInfo, CarryallInfo info) { - OrderID = "DeliverUnit"; - OrderPriority = 6; - this.carryableOffset = carryableOffset; this.aircraftInfo = aircraftInfo; this.info = info; } - public override bool CanTarget(Actor self, Target target, List othersAtTarget, ref TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, ref TargetModifiers modifiers, ref string cursor) { if (!info.AllowDropOff || !modifiers.HasModifier(TargetModifiers.ForceMove)) return false; diff --git a/OpenRA.Mods.Common/Traits/EntersTunnels.cs b/OpenRA.Mods.Common/Traits/EntersTunnels.cs index e3cef10545..7c08674455 100644 --- a/OpenRA.Mods.Common/Traits/EntersTunnels.cs +++ b/OpenRA.Mods.Common/Traits/EntersTunnels.cs @@ -9,16 +9,18 @@ */ #endregion +using System; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Orders; using OpenRA.Primitives; +using OpenRA.Support; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("This actor can interact with TunnelEntrances to move through TerrainTunnels.")] - public class EntersTunnelsInfo : ITraitInfo, Requires + public class EntersTunnelsInfo : ITraitInfo, Requires, IObservesVariablesInfo { public readonly string EnterCursor = "enter"; public readonly string EnterBlockedCursor = "enter-blocked"; @@ -26,13 +28,18 @@ namespace OpenRA.Mods.Common.Traits [VoiceReference] public readonly string Voice = "Action"; + [ConsumedConditionReference] + [Desc("Boolean expression defining the condition under which the regular (non-force) enter cursor is disabled.")] + public readonly BooleanExpression RequireForceMoveCondition = null; + public object Create(ActorInitializer init) { return new EntersTunnels(init.Self, this); } } - public class EntersTunnels : IIssueOrder, IResolveOrder, IOrderVoice + public class EntersTunnels : IIssueOrder, IResolveOrder, IOrderVoice, IObservesVariables { readonly EntersTunnelsInfo info; readonly IMove move; + bool requireForceMove; public EntersTunnels(Actor self, EntersTunnelsInfo info) { @@ -44,7 +51,7 @@ namespace OpenRA.Mods.Common.Traits { get { - yield return new EnterTunnelOrderTargeter(info.EnterCursor, info.EnterBlockedCursor); + yield return new EnterTunnelOrderTargeter(info.EnterCursor, info.EnterBlockedCursor, () => requireForceMove); } } @@ -78,21 +85,34 @@ namespace OpenRA.Mods.Common.Traits self.QueueActivity(move.MoveTo(tunnel.Exit.Value, tunnel.NearEnough)); } + IEnumerable IObservesVariables.GetVariableObservers() + { + if (info.RequireForceMoveCondition != null) + yield return new VariableObserver(RequireForceMoveConditionChanged, info.RequireForceMoveCondition.Variables); + } + + void RequireForceMoveConditionChanged(Actor self, IReadOnlyDictionary conditions) + { + requireForceMove = info.RequireForceMoveCondition.Evaluate(conditions); + } + public class EnterTunnelOrderTargeter : UnitOrderTargeter { readonly string enterCursor; readonly string enterBlockedCursor; + readonly Func requireForceMove; - public EnterTunnelOrderTargeter(string enterCursor, string enterBlockedCursor) + public EnterTunnelOrderTargeter(string enterCursor, string enterBlockedCursor, Func requireForceMove) : base("EnterTunnel", 6, enterCursor, true, true) { this.enterCursor = enterCursor; this.enterBlockedCursor = enterBlockedCursor; + this.requireForceMove = requireForceMove; } public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (target == null || target.IsDead) + if (target == null || target.IsDead || (requireForceMove() && !modifiers.HasModifier(TargetModifiers.ForceMove))) return false; var tunnel = target.TraitOrDefault(); diff --git a/OpenRA.Mods.Common/Traits/Harvester.cs b/OpenRA.Mods.Common/Traits/Harvester.cs index 2fef051a08..a9a2ff4f30 100644 --- a/OpenRA.Mods.Common/Traits/Harvester.cs +++ b/OpenRA.Mods.Common/Traits/Harvester.cs @@ -299,7 +299,7 @@ namespace OpenRA.Mods.Common.Traits get { yield return new EnterAlliedActorTargeter("Deliver", 5, - proc => IsAcceptableProcType(proc), + (proc, _) => IsAcceptableProcType(proc), proc => proc.Trait().AllowDocking); yield return new HarvestOrderTargeter(); } diff --git a/OpenRA.Mods.Common/Traits/Passenger.cs b/OpenRA.Mods.Common/Traits/Passenger.cs index 3c8c7c3dc2..be15a43f90 100644 --- a/OpenRA.Mods.Common/Traits/Passenger.cs +++ b/OpenRA.Mods.Common/Traits/Passenger.cs @@ -14,12 +14,13 @@ using System.Collections.Generic; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Orders; using OpenRA.Primitives; +using OpenRA.Support; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("This actor can enter Cargo actors.")] - public class PassengerInfo : ITraitInfo + public class PassengerInfo : ITraitInfo, IObservesVariablesInfo { public readonly string CargoType = null; public readonly PipType PipType = PipType.Green; @@ -39,13 +40,18 @@ namespace OpenRA.Mods.Common.Traits [VoiceReference] public readonly string Voice = "Action"; + [ConsumedConditionReference] + [Desc("Boolean expression defining the condition under which the regular (non-force) enter cursor is disabled.")] + public readonly BooleanExpression RequireForceMoveCondition = null; + public object Create(ActorInitializer init) { return new Passenger(this); } } - public class Passenger : INotifyCreated, IIssueOrder, IResolveOrder, IOrderVoice, INotifyRemovedFromWorld, INotifyEnteredCargo, INotifyExitedCargo, INotifyKilled + public class Passenger : INotifyCreated, IIssueOrder, IResolveOrder, IOrderVoice, INotifyRemovedFromWorld, INotifyEnteredCargo, INotifyExitedCargo, INotifyKilled, IObservesVariables { public readonly PassengerInfo Info; public Actor Transport; + bool requireForceMove; ConditionManager conditionManager; int anyCargoToken = ConditionManager.InvalidConditionToken; @@ -79,6 +85,14 @@ namespace OpenRA.Mods.Common.Traits return null; } + bool IsCorrectCargoType(Actor target, TargetModifiers modifiers) + { + if (requireForceMove && !modifiers.HasModifier(TargetModifiers.ForceMove)) + return false; + + return IsCorrectCargoType(target); + } + bool IsCorrectCargoType(Actor target) { var ci = target.Info.TraitInfo(); @@ -128,7 +142,7 @@ namespace OpenRA.Mods.Common.Traits specificCargoToken = conditionManager.RevokeCondition(self, specificCargoToken); } - public void ResolveOrder(Actor self, Order order) + void IResolveOrder.ResolveOrder(Actor self, Order order) { if (order.OrderString != "EnterTransport") return; @@ -180,5 +194,16 @@ namespace OpenRA.Mods.Common.Traits if (!Transport.IsDead) Transport.Trait().Unload(Transport, self); } + + IEnumerable IObservesVariables.GetVariableObservers() + { + if (Info.RequireForceMoveCondition != null) + yield return new VariableObserver(RequireForceMoveConditionChanged, Info.RequireForceMoveCondition.Variables); + } + + void RequireForceMoveConditionChanged(Actor self, IReadOnlyDictionary conditions) + { + requireForceMove = Info.RequireForceMoveCondition.Evaluate(conditions); + } } } diff --git a/OpenRA.Mods.Common/Traits/Repairable.cs b/OpenRA.Mods.Common/Traits/Repairable.cs index 541e11369f..3b69337d8b 100644 --- a/OpenRA.Mods.Common/Traits/Repairable.cs +++ b/OpenRA.Mods.Common/Traits/Repairable.cs @@ -15,12 +15,13 @@ using OpenRA.Activities; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Orders; using OpenRA.Primitives; +using OpenRA.Support; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("This actor can be sent to a structure for repairs.")] - public class RepairableInfo : ITraitInfo, Requires, Requires + public class RepairableInfo : ITraitInfo, Requires, Requires, IObservesVariablesInfo { [ActorReference] [FieldLoader.Require] @@ -32,15 +33,20 @@ namespace OpenRA.Mods.Common.Traits [Desc("The amount the unit will be repaired at each step. Use -1 for fallback behavior where HpPerStep from RepairsUnits trait will be used.")] public readonly int HpPerStep = -1; + [ConsumedConditionReference] + [Desc("Boolean expression defining the condition under which the regular (non-force) enter cursor is disabled.")] + public readonly BooleanExpression RequireForceMoveCondition = null; + public virtual object Create(ActorInitializer init) { return new Repairable(init.Self, this); } } - public class Repairable : IIssueOrder, IResolveOrder, IOrderVoice, INotifyCreated + public class Repairable : IIssueOrder, IResolveOrder, IOrderVoice, INotifyCreated, IObservesVariables { public readonly RepairableInfo Info; readonly IHealth health; readonly IMove movement; Rearmable rearmable; + bool requireForceMove; public Repairable(Actor self, RepairableInfo info) { @@ -75,9 +81,12 @@ namespace OpenRA.Mods.Common.Traits return Info.RepairActors.Contains(target.Info.Name); } - bool CanRearmAt(Actor target) + bool CanRepairAt(Actor target, TargetModifiers modifiers) { - return rearmable != null && rearmable.Info.RearmActors.Contains(target.Info.Name); + if (requireForceMove && !modifiers.HasModifier(TargetModifiers.ForceMove)) + return false; + + return Info.RepairActors.Contains(target.Info.Name); } bool CanRepair() @@ -95,7 +104,7 @@ namespace OpenRA.Mods.Common.Traits return order.OrderString == "Repair" && (CanRepair() || CanRearm()) ? Info.Voice : null; } - public void ResolveOrder(Actor self, Order order) + void IResolveOrder.ResolveOrder(Actor self, Order order) { if (order.OrderString == "Repair") { @@ -174,5 +183,16 @@ namespace OpenRA.Mods.Common.Traits foreach (var t in transports) t.RequestTransport(self, targetCell, nextActivity); } + + IEnumerable IObservesVariables.GetVariableObservers() + { + if (Info.RequireForceMoveCondition != null) + yield return new VariableObserver(RequireForceMoveConditionChanged, Info.RequireForceMoveCondition.Variables); + } + + void RequireForceMoveConditionChanged(Actor self, IReadOnlyDictionary conditions) + { + requireForceMove = Info.RequireForceMoveCondition.Evaluate(conditions); + } } } diff --git a/OpenRA.Mods.Common/Traits/RepairableNear.cs b/OpenRA.Mods.Common/Traits/RepairableNear.cs index e6badece92..7b4dbcd1da 100644 --- a/OpenRA.Mods.Common/Traits/RepairableNear.cs +++ b/OpenRA.Mods.Common/Traits/RepairableNear.cs @@ -14,11 +14,12 @@ using System.Linq; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Orders; using OpenRA.Primitives; +using OpenRA.Support; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - class RepairableNearInfo : ITraitInfo, Requires, Requires + class RepairableNearInfo : ITraitInfo, Requires, Requires, IObservesVariablesInfo { [ActorReference] [FieldLoader.Require] @@ -29,14 +30,19 @@ namespace OpenRA.Mods.Common.Traits [VoiceReference] public readonly string Voice = "Action"; + [ConsumedConditionReference] + [Desc("Boolean expression defining the condition under which the regular (non-force) enter cursor is disabled.")] + public readonly BooleanExpression RequireForceMoveCondition = null; + public object Create(ActorInitializer init) { return new RepairableNear(init.Self, this); } } - class RepairableNear : IIssueOrder, IResolveOrder, IOrderVoice + class RepairableNear : IIssueOrder, IResolveOrder, IOrderVoice, IObservesVariables { public readonly RepairableNearInfo Info; readonly Actor self; readonly IMove movement; + bool requireForceMove; public RepairableNear(Actor self, RepairableNearInfo info) { @@ -50,7 +56,7 @@ namespace OpenRA.Mods.Common.Traits get { yield return new EnterAlliedActorTargeter("RepairNear", 5, - target => CanRepairAt(target), _ => ShouldRepair()); + CanRepairAt, _ => ShouldRepair()); } } @@ -62,6 +68,14 @@ namespace OpenRA.Mods.Common.Traits return null; } + bool CanRepairAt(Actor target, TargetModifiers modifiers) + { + if (requireForceMove && !modifiers.HasModifier(TargetModifiers.ForceMove)) + return false; + + return Info.RepairActors.Contains(target.Info.Name); + } + bool CanRepairAt(Actor target) { return Info.RepairActors.Contains(target.Info.Name); @@ -108,5 +122,16 @@ namespace OpenRA.Mods.Common.Traits // Worst case FirstOrDefault() will return a TraitPair, which is OK. return repairBuilding.FirstOrDefault().Actor; } + + IEnumerable IObservesVariables.GetVariableObservers() + { + if (Info.RequireForceMoveCondition != null) + yield return new VariableObserver(RequireForceMoveConditionChanged, Info.RequireForceMoveCondition.Variables); + } + + void RequireForceMoveConditionChanged(Actor self, IReadOnlyDictionary conditions) + { + requireForceMove = Info.RequireForceMoveCondition.Evaluate(conditions); + } } } diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index aae2502db5..9e69070a22 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -32,9 +32,11 @@ FACT: TransformsIntoPassenger: RequiresCondition: factundeploy CargoType: Vehicle + RequiresForceMove: true TransformsIntoRepairable: RequiresCondition: factundeploy RepairActors: fix + RequiresForceMove: true GrantConditionOnPrerequisite@GLOBALFACTUNDEPLOY: Condition: factundeploy Prerequisites: global-factundeploy diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 61766822d1..33a3db6ba2 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -1149,9 +1149,11 @@ FACT: TransformsIntoPassenger: RequiresCondition: factundeploy CargoType: Vehicle + RequiresForceMove: true TransformsIntoRepairable: RequiresCondition: factundeploy RepairActors: fix + RequiresForceMove: true Sellable: RequiresCondition: !build-incomplete && !chrono-vortex && !being-captured && !being-demolished GrantConditionOnPrerequisite@GLOBALFACTUNDEPLOY: diff --git a/mods/ts/rules/gdi-vehicles.yaml b/mods/ts/rules/gdi-vehicles.yaml index 052a9b29b4..17eefc9e06 100644 --- a/mods/ts/rules/gdi-vehicles.yaml +++ b/mods/ts/rules/gdi-vehicles.yaml @@ -358,6 +358,12 @@ JUGG: DeploySounds: place2.aud UndeploySounds: clicky1.aud Voice: Move + EntersTunnels: + RequireForceMoveCondition: !undeployed + Repairable: + RequireForceMoveCondition: !undeployed + Passenger: + RequireForceMoveCondition: !undeployed GrantCondition@PREVIEWWORKAROUND: Condition: real-actor QuantizeFacingsFromSequence: diff --git a/mods/ts/rules/nod-vehicles.yaml b/mods/ts/rules/nod-vehicles.yaml index ebbbb4f291..46bf23c906 100644 --- a/mods/ts/rules/nod-vehicles.yaml +++ b/mods/ts/rules/nod-vehicles.yaml @@ -141,6 +141,12 @@ TTNK: DeploySounds: place2.aud UndeploySounds: clicky1.aud Voice: Move + EntersTunnels: + RequireForceMoveCondition: !undeployed + Repairable: + RequireForceMoveCondition: !undeployed + Passenger: + RequireForceMoveCondition: !undeployed GrantCondition@PREVIEWWORKAROUND: Condition: real-actor WithVoxelBody: @@ -249,6 +255,12 @@ ART2: DeploySounds: place2.aud UndeploySounds: clicky1.aud Voice: Move + EntersTunnels: + RequireForceMoveCondition: !undeployed + Repairable: + RequireForceMoveCondition: !undeployed + Passenger: + RequireForceMoveCondition: !undeployed GrantCondition@PREVIEWWORKAROUND: Condition: real-actor WithVoxelBody: diff --git a/mods/ts/rules/shared-structures.yaml b/mods/ts/rules/shared-structures.yaml index b8e6bd19ad..feb47f0f0a 100644 --- a/mods/ts/rules/shared-structures.yaml +++ b/mods/ts/rules/shared-structures.yaml @@ -43,13 +43,16 @@ GACNST: RequiresCondition: factundeploy RepairActors: gadept Voice: Move + RequiresForceMove: true TransformsIntoEntersTunnels: RequiresCondition: factundeploy Voice: Move + RequiresForceMove: true TransformsIntoPassenger: RequiresCondition: factundeploy CargoType: Vehicle Voice: Move + RequiresForceMove: true GrantConditionOnPrerequisite@GLOBALFACTUNDEPLOY: Condition: factundeploy Prerequisites: global-factundeploy diff --git a/mods/ts/rules/shared-vehicles.yaml b/mods/ts/rules/shared-vehicles.yaml index 1aa6ec08ef..a848fe497e 100644 --- a/mods/ts/rules/shared-vehicles.yaml +++ b/mods/ts/rules/shared-vehicles.yaml @@ -154,6 +154,12 @@ LPST: DeploySounds: place2.aud UndeploySounds: clicky1.aud Voice: Move + EntersTunnels: + RequireForceMoveCondition: !undeployed + Repairable: + RequireForceMoveCondition: !undeployed + Passenger: + RequireForceMoveCondition: !undeployed WithVoxelBody: RequiresCondition: undeployed WithSpriteBody@deployed: