diff --git a/OpenRA.Game/Traits/LintAttributes.cs b/OpenRA.Game/Traits/LintAttributes.cs index b89efab64d..1e84d58785 100644 --- a/OpenRA.Game/Traits/LintAttributes.cs +++ b/OpenRA.Game/Traits/LintAttributes.cs @@ -63,6 +63,17 @@ namespace OpenRA.Traits } } + [AttributeUsage(AttributeTargets.Field)] + public sealed class CursorReferenceAttribute : Attribute + { + public readonly LintDictionaryReference DictionaryReference; + + public CursorReferenceAttribute(LintDictionaryReference dictionaryReference = LintDictionaryReference.None) + { + DictionaryReference = dictionaryReference; + } + } + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class GrantedConditionReferenceAttribute : Attribute { } diff --git a/OpenRA.Mods.Cnc/Traits/Disguise.cs b/OpenRA.Mods.Cnc/Traits/Disguise.cs index d83dc8545c..83e627d19b 100644 --- a/OpenRA.Mods.Cnc/Traits/Disguise.cs +++ b/OpenRA.Mods.Cnc/Traits/Disguise.cs @@ -88,6 +88,7 @@ namespace OpenRA.Mods.Cnc.Traits "A dictionary of [actor id]: [condition].")] public readonly Dictionary DisguisedAsConditions = new Dictionary(); + [CursorReference] [Desc("Cursor to display when hovering over a valid actor to disguise as.")] public readonly string Cursor = "ability"; diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs index f265899eb1..a63034bb78 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs @@ -44,6 +44,7 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Experience to grant to the infiltrating player.")] public readonly int PlayerExperience = 0; + [CursorReference] [Desc("Cursor to display when able to infiltrate the target actor.")] public readonly string EnterCursor = "enter"; diff --git a/OpenRA.Mods.Cnc/Traits/MadTank.cs b/OpenRA.Mods.Cnc/Traits/MadTank.cs index 5bd7f04f56..df488a2aca 100644 --- a/OpenRA.Mods.Cnc/Traits/MadTank.cs +++ b/OpenRA.Mods.Cnc/Traits/MadTank.cs @@ -61,7 +61,15 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Types of damage that this trait causes to self while self-destructing. Leave empty for no damage types.")] public readonly BitSet DamageTypes = default(BitSet); - public override object Create(ActorInitializer init) { return new MadTank(init.Self, this); } + [CursorReference] + [Desc("Cursor to display when targetting.")] + public readonly string AttackCursor = "attack"; + + [CursorReference] + [Desc("Cursor to display when able to set up the detonation sequence.")] + public readonly string DeployCursor = "deploy"; + + public override object Create(ActorInitializer init) { return new MadTank(this); } public void RulesetLoaded(Ruleset rules, ActorInfo ai) { @@ -83,7 +91,9 @@ namespace OpenRA.Mods.Cnc.Traits { readonly MadTankInfo info; - public MadTank(Actor self, MadTankInfo info) + bool initiated; + + public MadTank(MadTankInfo info) { this.info = info; } @@ -92,8 +102,10 @@ namespace OpenRA.Mods.Cnc.Traits { get { - yield return new TargetTypeOrderTargeter(new BitSet("DetonateAttack"), "DetonateAttack", 5, "attack", true, false) { ForceAttack = false }; - yield return new DeployOrderTargeter("Detonate", 5); + yield return new TargetTypeOrderTargeter(new BitSet("DetonateAttack"), "DetonateAttack", 5, info.AttackCursor, true, false) { ForceAttack = false }; + + if (!initiated) + yield return new DeployOrderTargeter("Detonate", 5, () => info.DeployCursor); } } @@ -140,7 +152,6 @@ namespace OpenRA.Mods.Cnc.Traits readonly bool assignTargetOnFirstRun; int ticks; - bool initiated; Target target; public DetonationSequence(Actor self, MadTank mad) @@ -176,7 +187,7 @@ namespace OpenRA.Mods.Cnc.Traits return false; } - if (!initiated) + if (!mad.initiated) { // If the target has died while we were moving, we should abort detonation. if (target.Type == TargetType.Invalid) @@ -189,7 +200,7 @@ namespace OpenRA.Mods.Cnc.Traits wfsb.PlayCustomAnimationRepeating(self, mad.info.ThumpSequence); IsInterruptible = false; - initiated = true; + mad.initiated = true; } if (++ticks % mad.info.ThumpInterval == 0) @@ -209,7 +220,7 @@ namespace OpenRA.Mods.Cnc.Traits protected override void OnLastRun(Actor self) { - if (!initiated) + if (!mad.initiated) return; Game.Sound.Play(SoundType.World, mad.info.DetonationSound, self.CenterPosition); diff --git a/OpenRA.Mods.Cnc/Traits/Minelayer.cs b/OpenRA.Mods.Cnc/Traits/Minelayer.cs index 673ff77da5..a222eed917 100644 --- a/OpenRA.Mods.Cnc/Traits/Minelayer.cs +++ b/OpenRA.Mods.Cnc/Traits/Minelayer.cs @@ -50,12 +50,18 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Only allow laying mines on listed terrain types. Leave empty to allow all terrain types.")] public readonly HashSet TerrainTypes = new HashSet(); + [CursorReference] [Desc("Cursor to display when able to lay a mine.")] public readonly string DeployCursor = "deploy"; + [CursorReference] [Desc("Cursor to display when unable to lay a mine.")] public readonly string DeployBlockedCursor = "deploy-blocked"; + [CursorReference] + [Desc("Cursor to display when able to lay a mine.")] + public readonly string AbilityCursor = "ability"; + public override object Create(ActorInitializer init) { return new Minelayer(init.Self, this); } } @@ -85,7 +91,7 @@ namespace OpenRA.Mods.Cnc.Traits { get { - yield return new BeginMinefieldOrderTargeter(); + yield return new BeginMinefieldOrderTargeter(Info.AbilityCursor); yield return new DeployOrderTargeter("PlaceMine", 5, () => IsCellAcceptable(self, self.Location) ? Info.DeployCursor : Info.DeployBlockedCursor); } } @@ -97,7 +103,7 @@ namespace OpenRA.Mods.Cnc.Traits case "BeginMinefield": var start = self.World.Map.CellContaining(target.CenterPosition); if (self.World.OrderGenerator is MinefieldOrderGenerator) - ((MinefieldOrderGenerator)self.World.OrderGenerator).AddMinelayer(self, start); + ((MinefieldOrderGenerator)self.World.OrderGenerator).AddMinelayer(self); else self.World.OrderGenerator = new MinefieldOrderGenerator(self, start, queued); @@ -204,6 +210,7 @@ namespace OpenRA.Mods.Cnc.Traits readonly float validAlpha, unknownAlpha, blockedAlpha; readonly CPos minefieldStart; readonly bool queued; + readonly string cursor; public MinefieldOrderGenerator(Actor a, CPos xy, bool queued) { @@ -251,9 +258,11 @@ namespace OpenRA.Mods.Cnc.Traits blockedTile = blockedSequence.GetSprite(0); blockedAlpha = blockedSequence.GetAlpha(0); } + + cursor = minelayer.Info.AbilityCursor; } - public void AddMinelayer(Actor a, CPos xy) + public void AddMinelayer(Actor a) { minelayers.Add(a); } @@ -331,7 +340,7 @@ namespace OpenRA.Mods.Cnc.Traits protected override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) { - return "ability"; + return cursor; } } @@ -339,6 +348,14 @@ namespace OpenRA.Mods.Cnc.Traits { public string OrderID => "BeginMinefield"; public int OrderPriority => 5; + + readonly string cursor; + + public BeginMinefieldOrderTargeter(string cursor) + { + this.cursor = cursor; + } + public bool TargetOverridesSelection(Actor self, in Target target, List actorsAt, CPos xy, TargetModifiers modifiers) { return true; } public bool CanTarget(Actor self, in Target target, List othersAtTarget, ref TargetModifiers modifiers, ref string cursor) @@ -350,7 +367,8 @@ namespace OpenRA.Mods.Cnc.Traits if (!self.World.Map.Contains(location)) return false; - cursor = "ability"; + cursor = this.cursor; + IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); return modifiers.HasModifier(TargetModifiers.ForceAttack); diff --git a/OpenRA.Mods.Cnc/Traits/PortableChrono.cs b/OpenRA.Mods.Cnc/Traits/PortableChrono.cs index 0b700e471f..656985f7f5 100644 --- a/OpenRA.Mods.Cnc/Traits/PortableChrono.cs +++ b/OpenRA.Mods.Cnc/Traits/PortableChrono.cs @@ -35,15 +35,19 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Sound to play when teleporting.")] public readonly string ChronoshiftSound = "chrotnk1.aud"; + [CursorReference] [Desc("Cursor to display when able to deploy the actor.")] public readonly string DeployCursor = "deploy"; + [CursorReference] [Desc("Cursor to display when unable to deploy the actor.")] public readonly string DeployBlockedCursor = "deploy-blocked"; + [CursorReference] [Desc("Cursor to display when targeting a teleport location.")] public readonly string TargetCursor = "chrono-target"; + [CursorReference] [Desc("Cursor to display when the targeted location is blocked.")] public readonly string TargetBlockedCursor = "move-blocked"; diff --git a/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs b/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs index bfbd62cb2d..2ba8a31453 100644 --- a/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs +++ b/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs @@ -48,12 +48,15 @@ namespace OpenRA.Mods.Cnc.Traits public readonly bool KillCargo = true; + [CursorReference] [Desc("Cursor to display when selecting targets for the chronoshift.")] public readonly string SelectionCursor = "chrono-select"; + [CursorReference] [Desc("Cursor to display when targeting an area for the chronoshift.")] public readonly string TargetCursor = "chrono-target"; + [CursorReference] [Desc("Cursor to display when the targeted area is blocked.")] public readonly string TargetBlockedCursor = "move-blocked"; diff --git a/OpenRA.Mods.Common/Lint/CheckCursors.cs b/OpenRA.Mods.Common/Lint/CheckCursors.cs new file mode 100644 index 0000000000..acd1d057cf --- /dev/null +++ b/OpenRA.Mods.Common/Lint/CheckCursors.cs @@ -0,0 +1,55 @@ +#region Copyright & License Information +/* + * Copyright 2007-2020 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; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Lint +{ + class CheckCursors : ILintRulesPass + { + public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + var fileSystem = modData.DefaultFileSystem; + var sequenceYaml = MiniYaml.Merge(modData.Manifest.Cursors.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s))); + var nodesDict = new MiniYaml(null, sequenceYaml).ToDictionary(); + + // Avoid using CursorProvider as it attempts to load palettes from the file system. + var cursors = new List(); + foreach (var s in nodesDict["Cursors"].Nodes) + foreach (var sequence in s.Value.Nodes) + cursors.Add(sequence.Key); + + foreach (var actorInfo in rules.Actors) + { + foreach (var traitInfo in actorInfo.Value.TraitInfos()) + { + var fields = traitInfo.GetType().GetFields(); + foreach (var field in fields) + { + var cursorReference = field.GetCustomAttributes(true).FirstOrDefault(); + if (cursorReference == null) + continue; + + var cursor = LintExts.GetFieldValues(traitInfo, field, emitError, cursorReference.DictionaryReference).FirstOrDefault(); + if (string.IsNullOrEmpty(cursor)) + continue; + + if (!cursors.Contains(cursor)) + emitError("Undefined cursor {0} for actor {1} with trait {2}.".F(cursor, actorInfo.Value.Name, traitInfo)); + } + } + } + } + } +} diff --git a/OpenRA.Mods.Common/Orders/BeaconOrderGenerator.cs b/OpenRA.Mods.Common/Orders/BeaconOrderGenerator.cs index efdc85452d..b516d9a4e7 100644 --- a/OpenRA.Mods.Common/Orders/BeaconOrderGenerator.cs +++ b/OpenRA.Mods.Common/Orders/BeaconOrderGenerator.cs @@ -30,7 +30,7 @@ namespace OpenRA.Mods.Common.Orders protected override IEnumerable RenderAnnotations(WorldRenderer wr, World world) { yield break; } protected override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) { - return "ability"; + return "ability"; // TODO: [CursorReference] } } } diff --git a/OpenRA.Mods.Common/Orders/DeployOrderTargeter.cs b/OpenRA.Mods.Common/Orders/DeployOrderTargeter.cs index d3c0c0c201..6c073374db 100644 --- a/OpenRA.Mods.Common/Orders/DeployOrderTargeter.cs +++ b/OpenRA.Mods.Common/Orders/DeployOrderTargeter.cs @@ -19,11 +19,6 @@ namespace OpenRA.Mods.Common.Orders { readonly Func cursor; - public DeployOrderTargeter(string order, int priority) - : this(order, priority, () => "deploy") - { - } - public DeployOrderTargeter(string order, int priority, Func cursor) { OrderID = order; diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index d48c90229c..15a149e9bd 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -160,9 +160,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Boolean expression defining the condition under which the regular (non-force) move cursor is disabled.")] public readonly BooleanExpression RequireForceMoveCondition = null; + [CursorReference] [Desc("Cursor to display when able to land at target building.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to land at target building.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Armament.cs b/OpenRA.Mods.Common/Traits/Armament.cs index 2542dbf79f..ab9c84e152 100644 --- a/OpenRA.Mods.Common/Traits/Armament.cs +++ b/OpenRA.Mods.Common/Traits/Armament.cs @@ -74,10 +74,12 @@ namespace OpenRA.Mods.Common.Traits // TODO: instead of having multiple Armaments and unique AttackBase, // an actor should be able to have multiple AttackBases with // a single corresponding Armament each + [CursorReference] [Desc("Cursor to display when hovering over a valid target.")] public readonly string Cursor = "attack"; // TODO: same as above + [CursorReference] [Desc("Cursor to display when hovering over a valid target that is outside of range.")] public readonly string OutsideRangeCursor = "attackoutsiderange"; diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs b/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs index 7afbec59ce..c03dc76c0c 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs @@ -27,9 +27,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Armament names")] public readonly string[] Armaments = { "primary", "secondary" }; + [CursorReference] [Desc("Cursor to display when hovering over a valid target.")] public readonly string Cursor = null; + [CursorReference] [Desc("Cursor to display when hovering over a valid target that is outside of range.")] public readonly string OutsideRangeCursor = null; diff --git a/OpenRA.Mods.Common/Traits/AttackMove.cs b/OpenRA.Mods.Common/Traits/AttackMove.cs index 36dacc6e75..c6eec6cbb6 100644 --- a/OpenRA.Mods.Common/Traits/AttackMove.cs +++ b/OpenRA.Mods.Common/Traits/AttackMove.cs @@ -38,6 +38,18 @@ namespace OpenRA.Mods.Common.Traits [Desc("Can the actor be ordered to move in to shroud?")] public readonly bool MoveIntoShroud = true; + [CursorReference] + public readonly string AttackMoveCursor = "attackmove"; + + [CursorReference] + public readonly string AttackMoveBlockedCursor = "attackmove-blocked"; + + [CursorReference] + public readonly string AssaultMoveCursor = "assaultmove"; + + [CursorReference] + public readonly string AssaultMoveBlockedCursor = "assaultmove-blocked"; + public override object Create(ActorInitializer init) { return new AttackMove(init.Self, this); } } @@ -137,16 +149,31 @@ namespace OpenRA.Mods.Common.Traits public override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) { - var prefix = mi.Modifiers.HasModifier(Modifiers.Ctrl) ? "assaultmove" : "attackmove"; + var isAssaultMove = mi.Modifiers.HasModifier(Modifiers.Ctrl); - if (world.Map.Contains(cell) && subjects.Any()) + var subject = subjects.FirstOrDefault(); + if (subject.Actor != null) { - var explored = subjects.First().Actor.Owner.Shroud.IsExplored(cell); - var blocked = !explored && subjects.Any(a => !a.Trait.Info.MoveIntoShroud); - return blocked ? prefix + "-blocked" : prefix; + var info = subject.Trait.Info; + if (world.Map.Contains(cell)) + { + var explored = subject.Actor.Owner.Shroud.IsExplored(cell); + var cannotMove = subjects.FirstOrDefault(a => !a.Trait.Info.MoveIntoShroud).Trait; + var blocked = !explored && cannotMove != null; + + if (isAssaultMove) + return blocked ? cannotMove.Info.AssaultMoveBlockedCursor : info.AssaultMoveCursor; + + return blocked ? cannotMove.Info.AttackMoveBlockedCursor : info.AttackMoveCursor; + } + + if (isAssaultMove) + return info.AssaultMoveBlockedCursor; + else + return info.AttackMoveBlockedCursor; } - return prefix + "-blocked"; + return null; } public override bool InputOverridesSelection(World world, int2 xy, MouseInput mi) diff --git a/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs b/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs index f7f428126f..c8e2f751d3 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs @@ -40,7 +40,11 @@ namespace OpenRA.Mods.Common.Traits "If empty, the list given in the `Produces` property of the `Production` trait will be used.")] public readonly string[] ProductionQueues = { }; - public override object Create(ActorInitializer init) { return new PrimaryBuilding(init.Self, this); } + [CursorReference] + [Desc("Cursor to display when setting the primary building.")] + public readonly string Cursor = "deploy"; + + public override object Create(ActorInitializer init) { return new PrimaryBuilding(this); } } public class PrimaryBuilding : ConditionalTrait, IIssueOrder, IResolveOrder @@ -51,7 +55,7 @@ namespace OpenRA.Mods.Common.Traits public bool IsPrimary { get; private set; } - public PrimaryBuilding(Actor self, PrimaryBuildingInfo info) + public PrimaryBuilding(PrimaryBuildingInfo info) : base(info) { } IEnumerable IIssueOrder.Orders @@ -61,7 +65,7 @@ namespace OpenRA.Mods.Common.Traits if (IsTraitDisabled) yield break; - yield return new DeployOrderTargeter(OrderID, 1); + yield return new DeployOrderTargeter(OrderID, 1, () => Info.Cursor); } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs b/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs index fb56ca55a3..7abd11dc9a 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs @@ -30,6 +30,7 @@ namespace OpenRA.Mods.Common.Traits [SequenceReference(nameof(Image), allowNullImage: true)] public readonly string CirclesSequence = "circles"; + [CursorReference] [Desc("Cursor to display when rally point can be set.")] public readonly string Cursor = "ability"; diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs index 355b099b6d..b81f8e3549 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs @@ -34,9 +34,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Require the force-move modifier to display the move cursor.")] public readonly bool RequiresForceMove = false; + [CursorReference] [Desc("Cursor to display when able to land at target building.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to land at target building.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoEntersTunnels.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoEntersTunnels.cs index 4363e911cc..2562367b08 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoEntersTunnels.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoEntersTunnels.cs @@ -20,9 +20,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Add to a building to expose a move cursor that triggers Transforms and issues an enter tunnel order to the transformed actor.")] public class TransformsIntoEntersTunnelsInfo : ConditionalTraitInfo, Requires { + [CursorReference] [Desc("Cursor to display when able to enter target tunnel.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to enter target tunnel.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs index caba137db0..2c49c379ca 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs @@ -25,9 +25,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Locomotor used by the transformed actor. Must be defined on the World actor.")] public readonly string Locomotor = null; + [CursorReference] [Desc("Cursor to display when a move order can be issued at target location.")] public readonly string Cursor = "move"; + [CursorReference] [Desc("Cursor to display when a move order cannot be issued at target location.")] public readonly string BlockedCursor = "move-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoPassenger.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoPassenger.cs index 47ef570cb6..a52b9e247e 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoPassenger.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoPassenger.cs @@ -33,9 +33,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Require the force-move modifier to display the enter cursor.")] public readonly bool RequiresForceMove = false; + [CursorReference] [Desc("Cursor to display when able to enter target actor.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to enter target actor.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs index 1d3544dab0..3e47af6c86 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs @@ -34,9 +34,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Require the force-move modifier to display the enter cursor.")] public readonly bool RequiresForceMove = false; + [CursorReference] [Desc("Cursor to display when able to be repaired at target actor.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to be repaired at target actor.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Captures.cs b/OpenRA.Mods.Common/Traits/Captures.cs index 309a9a3d69..ef661e5fc4 100644 --- a/OpenRA.Mods.Common/Traits/Captures.cs +++ b/OpenRA.Mods.Common/Traits/Captures.cs @@ -46,12 +46,15 @@ namespace OpenRA.Mods.Common.Traits [Desc("Relationships that the structure's previous owner needs to have for the capturing player to receive Experience.")] public readonly PlayerRelationship PlayerExperienceRelationships = PlayerRelationship.Enemy; + [CursorReference] [Desc("Cursor to display when the health of the target actor is above the sabotage threshold.")] public readonly string SabotageCursor = "capture"; + [CursorReference] [Desc("Cursor to display when able to capture the target actor.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to capture the target actor.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Cargo.cs b/OpenRA.Mods.Common/Traits/Cargo.cs index cd01c1a921..5b72235c46 100644 --- a/OpenRA.Mods.Common/Traits/Cargo.cs +++ b/OpenRA.Mods.Common/Traits/Cargo.cs @@ -60,9 +60,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Delay (in ticks) before continuing after unloading a passenger.")] public readonly int AfterUnloadDelay = 25; + [CursorReference] [Desc("Cursor to display when able to unload the passengers.")] public readonly string UnloadCursor = "deploy"; + [CursorReference] [Desc("Cursor to display when unable to unload the passengers.")] public readonly string UnloadBlockedCursor = "deploy-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Carryall.cs b/OpenRA.Mods.Common/Traits/Carryall.cs index fb14c1d4ad..f1663032ad 100644 --- a/OpenRA.Mods.Common/Traits/Carryall.cs +++ b/OpenRA.Mods.Common/Traits/Carryall.cs @@ -39,21 +39,26 @@ namespace OpenRA.Mods.Common.Traits [Desc("Radius around the target drop location that are considered if the target tile is blocked.")] public readonly WDist DropRange = WDist.FromCells(5); + [CursorReference] [Desc("Cursor to display when able to unload the passengers.")] public readonly string UnloadCursor = "deploy"; + [CursorReference] [Desc("Cursor to display when unable to unload the passengers.")] public readonly string UnloadBlockedCursor = "deploy-blocked"; [Desc("Allow moving and unloading with one order using force-move")] public readonly bool AllowDropOff = false; + [CursorReference] [Desc("Cursor to display when able to drop off the passengers at location.")] public readonly string DropOffCursor = "ability"; + [CursorReference] [Desc("Cursor to display when unable to drop off the passengers at location.")] public readonly string DropOffBlockedCursor = "move-blocked"; + [CursorReference] [Desc("Cursor to display when picking up the passengers.")] public readonly string PickUpCursor = "ability"; diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs index 2e68bddf20..4a976258ca 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs @@ -37,9 +37,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Can this actor deploy on slopes?")] public readonly bool CanDeployOnRamps = false; + [CursorReference] [Desc("Cursor to display when able to (un)deploy the actor.")] public readonly string DeployCursor = "deploy"; + [CursorReference] [Desc("Cursor to display when unable to (un)deploy the actor.")] public readonly string DeployBlockedCursor = "deploy-blocked"; diff --git a/OpenRA.Mods.Common/Traits/DeliversCash.cs b/OpenRA.Mods.Common/Traits/DeliversCash.cs index 1bb49fde43..15fca0ee56 100644 --- a/OpenRA.Mods.Common/Traits/DeliversCash.cs +++ b/OpenRA.Mods.Common/Traits/DeliversCash.cs @@ -32,6 +32,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Sound to play when delivering cash")] public readonly string[] Sounds = { }; + [CursorReference] [Desc("Cursor to display when hovering over a valid actor to deliver cash to.")] public readonly string Cursor = "enter"; diff --git a/OpenRA.Mods.Common/Traits/DeliversExperience.cs b/OpenRA.Mods.Common/Traits/DeliversExperience.cs index 4f94009fb3..43574cb95f 100644 --- a/OpenRA.Mods.Common/Traits/DeliversExperience.cs +++ b/OpenRA.Mods.Common/Traits/DeliversExperience.cs @@ -26,6 +26,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Identifier checked against AcceptsDeliveredExperience.ValidTypes. Only needed if the latter is not empty.")] public readonly string Type = null; + [CursorReference] [Desc("Cursor to display when hovering over a valid actor to deliver experience to.")] public readonly string Cursor = "enter"; diff --git a/OpenRA.Mods.Common/Traits/Demolition.cs b/OpenRA.Mods.Common/Traits/Demolition.cs index ef2240690d..409a3fd5b2 100644 --- a/OpenRA.Mods.Common/Traits/Demolition.cs +++ b/OpenRA.Mods.Common/Traits/Demolition.cs @@ -50,6 +50,7 @@ namespace OpenRA.Mods.Common.Traits public readonly PlayerRelationship TargetRelationships = PlayerRelationship.Enemy | PlayerRelationship.Neutral; public readonly PlayerRelationship ForceTargetRelationships = PlayerRelationship.Enemy | PlayerRelationship.Neutral | PlayerRelationship.Ally; + [CursorReference] [Desc("Cursor to display when hovering over a demolishable target.")] public readonly string Cursor = "c4"; diff --git a/OpenRA.Mods.Common/Traits/EngineerRepair.cs b/OpenRA.Mods.Common/Traits/EngineerRepair.cs index 9f72fe918a..cc8a157d9b 100644 --- a/OpenRA.Mods.Common/Traits/EngineerRepair.cs +++ b/OpenRA.Mods.Common/Traits/EngineerRepair.cs @@ -39,9 +39,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Sound to play when repairing is done.")] public readonly string RepairSound = null; + [CursorReference] [Desc("Cursor to display when hovering over a valid actor to repair.")] public readonly string Cursor = "goldwrench"; + [CursorReference] [Desc("Cursor to display when target actor has full health so it can't be repaired.")] public readonly string RepairBlockedCursor = "goldwrench-blocked"; diff --git a/OpenRA.Mods.Common/Traits/EntersTunnels.cs b/OpenRA.Mods.Common/Traits/EntersTunnels.cs index 33ff47a9d1..7de6673d4c 100644 --- a/OpenRA.Mods.Common/Traits/EntersTunnels.cs +++ b/OpenRA.Mods.Common/Traits/EntersTunnels.cs @@ -22,9 +22,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("This actor can interact with TunnelEntrances to move through TerrainTunnels.")] public class EntersTunnelsInfo : TraitInfo, Requires, IObservesVariablesInfo { + [CursorReference] [Desc("Cursor to display when able to enter target tunnel.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to enter target tunnel.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Harvester.cs b/OpenRA.Mods.Common/Traits/Harvester.cs index e6134a53bb..cd6ea2e552 100644 --- a/OpenRA.Mods.Common/Traits/Harvester.cs +++ b/OpenRA.Mods.Common/Traits/Harvester.cs @@ -90,9 +90,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Color to use for the target line of harvest orders.")] public readonly Color DeliverLineColor = Color.Green; + [CursorReference] [Desc("Cursor to display when able to unload at target actor.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to unload at target actor.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index b721be86a0..b4b238e4bc 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -40,9 +40,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("If set to true, this unit will always turn in place instead of following a curved trajectory (like infantry).")] public readonly bool AlwaysTurnInPlace = false; + [CursorReference] [Desc("Cursor to display when a move order can be issued at target location.")] public readonly string Cursor = "move"; + [CursorReference] [Desc("Cursor to display when a move order cannot be issued at target location.")] public readonly string BlockedCursor = "move-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Passenger.cs b/OpenRA.Mods.Common/Traits/Passenger.cs index 0c5308fb80..34dc8d90f1 100644 --- a/OpenRA.Mods.Common/Traits/Passenger.cs +++ b/OpenRA.Mods.Common/Traits/Passenger.cs @@ -50,9 +50,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Boolean expression defining the condition under which the regular (non-force) enter cursor is disabled.")] public readonly BooleanExpression RequireForceMoveCondition = null; + [CursorReference] [Desc("Cursor to display when able to enter target actor.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to enter target actor.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Repairable.cs b/OpenRA.Mods.Common/Traits/Repairable.cs index 70ecabef45..9bee0ece8b 100644 --- a/OpenRA.Mods.Common/Traits/Repairable.cs +++ b/OpenRA.Mods.Common/Traits/Repairable.cs @@ -35,9 +35,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Boolean expression defining the condition under which the regular (non-force) enter cursor is disabled.")] public readonly BooleanExpression RequireForceMoveCondition = null; + [CursorReference] [Desc("Cursor to display when able to be repaired at target actor.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to be repaired at target actor.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/RepairableNear.cs b/OpenRA.Mods.Common/Traits/RepairableNear.cs index d35b7e9482..6187f75931 100644 --- a/OpenRA.Mods.Common/Traits/RepairableNear.cs +++ b/OpenRA.Mods.Common/Traits/RepairableNear.cs @@ -33,9 +33,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Boolean expression defining the condition under which the regular (non-force) enter cursor is disabled.")] public readonly BooleanExpression RequireForceMoveCondition = null; + [CursorReference] [Desc("Cursor to display when able to be repaired near target actor.")] public readonly string EnterCursor = "enter"; + [CursorReference] [Desc("Cursor to display when unable to be repaired near target actor.")] public readonly string EnterBlockedCursor = "enter-blocked"; diff --git a/OpenRA.Mods.Common/Traits/RepairsBridges.cs b/OpenRA.Mods.Common/Traits/RepairsBridges.cs index 692de073bb..bd4675651e 100644 --- a/OpenRA.Mods.Common/Traits/RepairsBridges.cs +++ b/OpenRA.Mods.Common/Traits/RepairsBridges.cs @@ -30,9 +30,11 @@ namespace OpenRA.Mods.Common.Traits "Possible values are Exit, Suicide, Dispose.")] public readonly EnterBehaviour EnterBehaviour = EnterBehaviour.Dispose; + [CursorReference] [Desc("Cursor to display when targeting an unrepaired bridge.")] public readonly string TargetCursor = "goldwrench"; + [CursorReference] [Desc("Cursor to display when repairing is denied.")] public readonly string TargetBlockedCursor = "goldwrench-blocked"; diff --git a/OpenRA.Mods.Common/Traits/Sellable.cs b/OpenRA.Mods.Common/Traits/Sellable.cs index da8ace8abf..6876874e43 100644 --- a/OpenRA.Mods.Common/Traits/Sellable.cs +++ b/OpenRA.Mods.Common/Traits/Sellable.cs @@ -39,6 +39,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Skip playing (reversed) make animation.")] public readonly bool SkipMakeAnimation = false; + [CursorReference] [Desc("Cursor to display when the sell order generator hovers over this actor.")] public readonly string Cursor = "sell"; diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs index c8bb02b4f2..7c5fdd6ea5 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs @@ -47,6 +47,7 @@ namespace OpenRA.Mods.Common.Traits "This requires the actor to have the WithSpriteBody trait or one of its derivatives.")] public readonly string Sequence = "active"; + [CursorReference] [Desc("Cursor to display when there are no units to apply the condition in range.")] public readonly string BlockedCursor = "move-blocked"; diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs index ef4fab6542..eaaf838332 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs @@ -38,6 +38,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Allow this to be used only once.")] public readonly bool OneShot = false; + [CursorReference] [Desc("Cursor to display for using this support power.")] public readonly string Cursor = "ability"; diff --git a/OpenRA.Mods.Common/Traits/Transforms.cs b/OpenRA.Mods.Common/Traits/Transforms.cs index b1921ee2ff..d21814b7c6 100644 --- a/OpenRA.Mods.Common/Traits/Transforms.cs +++ b/OpenRA.Mods.Common/Traits/Transforms.cs @@ -45,9 +45,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Notification to play when the transformation is blocked.")] public readonly string NoTransformNotification = null; + [CursorReference] [Desc("Cursor to display when able to (un)deploy the actor.")] public readonly string DeployCursor = "deploy"; + [CursorReference] [Desc("Cursor to display when unable to (un)deploy the actor.")] public readonly string DeployBlockedCursor = "deploy-blocked";