diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index 4663496448..55d0e0f8af 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -190,12 +190,8 @@ namespace OpenRA.Graphics public void DrawRollover(Actor unit) { - var selectable = unit.TraitOrDefault(); - if (selectable != null) - { - if (selectable.Info.Selectable) - new SelectionBarsRenderable(unit).Render(this); - } + if (unit.HasTrait()) + new SelectionBarsRenderable(unit).Render(this); } public void DrawRangeCircle(WPos pos, WRange range, Color c) diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index faaee0190d..899d6ba3f3 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -63,8 +63,7 @@ namespace OpenRA.Orders if (underCursor != null && (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())) { - var selectable = underCursor.TraitOrDefault(); - if (selectable != null && selectable.Info.Selectable) + if (underCursor.HasTrait()) useSelect = true; } diff --git a/OpenRA.Game/Traits/Selectable.cs b/OpenRA.Game/Traits/Selectable.cs index 6ba7b12f39..fa9fc9be10 100644 --- a/OpenRA.Game/Traits/Selectable.cs +++ b/OpenRA.Game/Traits/Selectable.cs @@ -15,9 +15,9 @@ using OpenRA.Graphics; namespace OpenRA.Traits { + [Desc("This actor is selectable. Defines bounds of selectable area and selection priority.")] public class SelectableInfo : ITraitInfo { - public readonly bool Selectable = true; public readonly int Priority = 10; [Desc("Bounds for the selectable area.")] public readonly int[] Bounds = null; diff --git a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs index ca07f6d9fb..af381f97e5 100644 --- a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs @@ -92,7 +92,7 @@ namespace OpenRA.Widgets { if (!hasBox && World.Selection.Actors.Any() && !multiClick) { - if (!(World.ScreenMap.ActorsAt(xy).Where(x => x.HasTrait() && x.Trait().Info.Selectable && + if (!(World.ScreenMap.ActorsAt(xy).Where(x => x.HasTrait() && (x.Owner.IsAlliedWith(World.RenderPlayer) || !World.FogObscures(x))).Any() && !mi.Modifiers.HasModifier(Modifiers.Ctrl) && !mi.Modifiers.HasModifier(Modifiers.Alt) && UnitOrderGenerator.InputOverridesSelection(World, xy, mi))) { @@ -292,7 +292,7 @@ namespace OpenRA.Widgets var s = a.TraitOrDefault(); // sc == null means that units, that meet all other criteria, get selected - return s != null && s.Info.Selectable && (selectionClasses == null || selectionClasses.Contains(s.Class)); + return s != null && (selectionClasses == null || selectionClasses.Contains(s.Class)); }); } @@ -303,11 +303,7 @@ namespace OpenRA.Widgets a = b; return world.ScreenMap.ActorsInBox(a, b) - .Where(x => - { - var s = x.TraitOrDefault(); - return s != null && s.Info.Selectable && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x)); - }) + .Where(x => x.HasTrait() && (x.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(x))) .GroupBy(x => x.GetSelectionPriority()) .OrderByDescending(g => g.Key) .Select(g => g.AsEnumerable()) diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 447dd44b70..d13f3a14c6 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -310,6 +310,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/CustomSelectionSize.cs b/OpenRA.Mods.Common/Traits/CustomSelectionSize.cs new file mode 100644 index 0000000000..159479d4e2 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/CustomSelectionSize.cs @@ -0,0 +1,36 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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 OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Special case trait for invisible, unselectable actors like bridge huts.", + "Gives actor targetable area for special cases like C4 and engineer repair.", + "This trait conflicts with AutoSelectionSize so you cannot use both, and doesn't support custom offsets.")] + public class CustomSelectionSizeInfo : ITraitInfo + { + public readonly int[] CustomBounds = null; + + public object Create(ActorInitializer init) { return new CustomSelectionSize(this); } + } + + public class CustomSelectionSize : IAutoSelectionSize + { + readonly CustomSelectionSizeInfo info; + public CustomSelectionSize(CustomSelectionSizeInfo info) { this.info = info; } + + public int2 SelectionSize(Actor self) + { + return new int2(info.CustomBounds[0], info.CustomBounds[1]); + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 37c63131d0..f88d7db7ab 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1175,6 +1175,28 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + if (engineVersion < 20150604) + { + if (depth == 1 && node.Value.Nodes.Exists(n => n.Key == "Selectable")) + { + var selectable = node.Value.Nodes.FirstOrDefault(n => n.Key == "Selectable"); + if (node.Key == "Selectable" && selectable.Value.Value == "false") + node.Key = "SelectableRemoveMe"; + + // To cover rare cases where the boolean was 'true' + if (node.Key == "Selectable" && selectable.Value.Value == "true") + node.Value.Nodes.Remove(selectable); + } + + if (depth == 0 && node.Value.Nodes.Exists(n => n.Key == "SelectableRemoveMe")) + node.Value.Nodes.RemoveAll(n => n.Key == "SelectableRemoveMe"); + Console.WriteLine("The 'Selectable' boolean has been removed from the Selectable trait."); + Console.WriteLine("If you just want to disable an inherited Selectable trait, use -Selectable instead."); + Console.WriteLine("For special cases like bridge huts, which need bounds to be targetable by C4 and engineers,"); + Console.WriteLine("give them the CustomSelectionSize trait with CustomBounds."); + Console.WriteLine("See RA and C&C bridge huts for reference."); + } + UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); } } diff --git a/mods/cnc/maps/nod04b/map.yaml b/mods/cnc/maps/nod04b/map.yaml index 1a7af70e06..9565289096 100644 --- a/mods/cnc/maps/nod04b/map.yaml +++ b/mods/cnc/maps/nod04b/map.yaml @@ -545,8 +545,7 @@ Rules: ShowOwnerRow: false TRAN: RejectsOrders: - Selectable: - Selectable: false + -Selectable: RevealsShroud: Range: 5c0 diff --git a/mods/cnc/rules/civilian.yaml b/mods/cnc/rules/civilian.yaml index f08639e6c3..c2097631c5 100644 --- a/mods/cnc/rules/civilian.yaml +++ b/mods/cnc/rules/civilian.yaml @@ -370,10 +370,8 @@ BRIDGEHUT: Building: Footprint: __ __ Dimensions: 2,2 - Selectable: - Selectable: false - Bounds: 48,48 - Priority: 2 + CustomSelectionSize: + CustomBounds: 48,48 BridgeHut: TargetableBuilding: TargetTypes: BridgeHut, C4 diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 42adf35550..ebf87f6e4d 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -672,9 +672,4 @@ Image: crate WithCrateBody: XmasImages: xcratea, xcrateb, xcratec, xcrated - Selectable: - Selectable: false - Bounds: 15,15,-1,-1 - SelectionDecorations: - VisualBounds: 15,15,-1,-1 diff --git a/mods/d2k/rules/misc.yaml b/mods/d2k/rules/misc.yaml index 19c3c43f02..dd74f52c95 100644 --- a/mods/d2k/rules/misc.yaml +++ b/mods/d2k/rules/misc.yaml @@ -109,12 +109,7 @@ crate: RenderSprites: Palette: effect WithCrateBody: - Selectable: - Selectable: false - Bounds: 15,15,-1,-1 Passenger: - SelectionDecorations: - VisualBounds: 15,15,-1,-1 mpspawn: Immobile: diff --git a/mods/ra/rules/civilian.yaml b/mods/ra/rules/civilian.yaml index 1441215d72..b365048b99 100644 --- a/mods/ra/rules/civilian.yaml +++ b/mods/ra/rules/civilian.yaml @@ -490,10 +490,8 @@ BRIDGEHUT: Building: Footprint: __ __ Dimensions: 2,2 - Selectable: - Selectable: false - Bounds: 48,48 - Priority: 2 + CustomSelectionSize: + CustomBounds: 48,48 BridgeHut: TargetableBuilding: TargetTypes: BridgeHut, C4 @@ -502,10 +500,8 @@ BRIDGEHUT.small: Building: Footprint: _ Dimensions: 1,1 - Selectable: - Selectable: false - Bounds: 24,24 - Priority: 2 + CustomSelectionSize: + CustomBounds: 24,24 BridgeHut: TargetableBuilding: TargetTypes: BridgeHut, C4 diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 07d2512a33..2cf670256c 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -665,9 +665,6 @@ Image: scrate WithCrateBody: XmasImages: xcratea, xcrateb, xcratec, xcrated - Selectable: - Selectable: false - Bounds: 15,15,-1,-1 Parachutable: KilledOnImpassableTerrain: false ParachuteSequence: parach diff --git a/mods/ra/rules/misc.yaml b/mods/ra/rules/misc.yaml index 4496de2172..2328f4441e 100644 --- a/mods/ra/rules/misc.yaml +++ b/mods/ra/rules/misc.yaml @@ -217,9 +217,6 @@ FLARE: Tooltip: Name: Flare ShowOwnerRow: false - Selectable: - Selectable: false - Bounds: 25,25 BodyOrientation: MINE: diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 44e446f955..bf30fd7362 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -136,11 +136,6 @@ Palette: terrain WithCrateBody: Images: crate - Selectable: - Selectable: false - Bounds: 25,25,-1,-1 - SelectionDecorations: - VisualBounds: 25,25,-1,-1 ^Wall: AppearsOnRadar: