diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 2de9feacc5..4a866151d1 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -333,7 +333,8 @@ namespace OpenRA foreach (var t in TraitsImplementing()) t.OnOwnerChanged(this, oldOwner, newOwner); - World.Selection.OnOwnerChanged(this, oldOwner, newOwner); + foreach (var t in World.WorldActor.TraitsImplementing()) + t.OnOwnerChanged(this, oldOwner, newOwner); if (wasInWorld) World.Add(this); diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 4c5b951441..f076f0d6fa 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -602,7 +602,6 @@ namespace OpenRA Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, () => { world.OrderGenerator.Tick(world); - world.Selection.Tick(world); }); world.Tick(); diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 1ef3ec7778..b208acad7c 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -413,6 +413,22 @@ namespace OpenRA.Traits bool SpatiallyPartitionable { get; } } + public interface ISelection + { + int Hash { get; } + IEnumerable Actors { get; } + + void Add(Actor a); + void Remove(Actor a); + bool Contains(Actor a); + void Combine(World world, IEnumerable newSelection, bool isCombine, bool isClick); + void Clear(); + void DoControlGroup(World world, WorldRenderer worldRenderer, int group, Modifiers mods, int multiTapCount); + void AddToControlGroup(Actor a, int group); + void RemoveFromControlGroup(Actor a); + int? GetControlGroupForActor(Actor a); + } + /// /// Indicates target types as defined on are present in a . /// diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index f56054cd77..f1c3b74587 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -155,7 +155,7 @@ namespace OpenRA } } - public readonly Selection Selection; + public readonly ISelection Selection; public void CancelInputMode() { OrderGenerator = new UnitOrderGenerator(); } @@ -193,8 +193,7 @@ namespace OpenRA WorldActor = CreateActor(worldActorType, new TypeDictionary()); ActorMap = WorldActor.Trait(); ScreenMap = WorldActor.Trait(); - - Selection = new Selection(WorldActor.TraitsImplementing()); + Selection = WorldActor.Trait(); // Add players foreach (var cmp in WorldActor.TraitsImplementing()) diff --git a/OpenRA.Mods.Common/Traits/World/GameSaveViewportManager.cs b/OpenRA.Mods.Common/Traits/World/GameSaveViewportManager.cs index 4ba112c44e..11c8761e3c 100644 --- a/OpenRA.Mods.Common/Traits/World/GameSaveViewportManager.cs +++ b/OpenRA.Mods.Common/Traits/World/GameSaveViewportManager.cs @@ -45,9 +45,6 @@ namespace OpenRA.Mods.Common.Traits if (localPlayer == null && renderPlayer != null) nodes.Add(new MiniYamlNode("RenderPlayer", FieldSaver.FormatValue(renderPlayer.PlayerActor.ActorID))); - if (localPlayer != null && localPlayer.PlayerActor == self) - nodes.Add(new MiniYamlNode("Selection", "", self.World.Selection.Serialize())); - return nodes; } @@ -57,10 +54,6 @@ namespace OpenRA.Mods.Common.Traits if (viewportNode != null) worldRenderer.Viewport.Center(FieldLoader.GetValue("Viewport", viewportNode.Value.Value)); - var selectionNode = data.FirstOrDefault(n => n.Key == "Selection"); - if (selectionNode != null) - self.World.Selection.Deserialize(self.World, selectionNode.Value.Nodes); - var renderPlayerNode = data.FirstOrDefault(n => n.Key == "RenderPlayer"); if (renderPlayerNode != null) { diff --git a/OpenRA.Game/Selection.cs b/OpenRA.Mods.Common/Traits/World/Selection.cs similarity index 82% rename from OpenRA.Game/Selection.cs rename to OpenRA.Mods.Common/Traits/World/Selection.cs index d67a16194c..9e932817e5 100644 --- a/OpenRA.Game/Selection.cs +++ b/OpenRA.Mods.Common/Traits/World/Selection.cs @@ -15,19 +15,26 @@ using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Traits; -namespace OpenRA +namespace OpenRA.Mods.Common.Traits { - public class Selection + public class SelectionInfo : ITraitInfo + { + public object Create(ActorInitializer init) { return new Selection(this); } + } + + public class Selection : ISelection, INotifyCreated, INotifyOwnerChanged, ITick, IGameSaveTraitData { public int Hash { get; private set; } public IEnumerable Actors { get { return actors; } } readonly HashSet actors = new HashSet(); - readonly INotifySelection[] worldNotifySelection; + INotifySelection[] worldNotifySelection; - internal Selection(IEnumerable worldNotifySelection) + public Selection(SelectionInfo info) { } + + void INotifyCreated.Created(Actor self) { - this.worldNotifySelection = worldNotifySelection.ToArray(); + this.worldNotifySelection = self.TraitsImplementing().ToArray(); } void UpdateHash() @@ -38,7 +45,7 @@ namespace OpenRA Hash += 1; } - public void Add(Actor a) + public virtual void Add(Actor a) { actors.Add(a); UpdateHash(); @@ -50,7 +57,7 @@ namespace OpenRA ns.SelectionChanged(); } - public void Remove(Actor a) + public virtual void Remove(Actor a) { if (actors.Remove(a)) { @@ -60,7 +67,7 @@ namespace OpenRA } } - internal void OnOwnerChanged(Actor a, Player oldOwner, Player newOwner) + void INotifyOwnerChanged.OnOwnerChanged(Actor a, Player oldOwner, Player newOwner) { if (!actors.Contains(a)) return; @@ -78,7 +85,7 @@ namespace OpenRA return actors.Contains(a); } - public void Combine(World world, IEnumerable newSelection, bool isCombine, bool isClick) + public virtual void Combine(World world, IEnumerable newSelection, bool isCombine, bool isClick) { if (isClick) { @@ -137,16 +144,16 @@ namespace OpenRA UpdateHash(); } - public void Tick(World world) + void ITick.Tick(Actor self) { - var removed = actors.RemoveWhere(a => !a.IsInWorld || (!a.Owner.IsAlliedWith(world.RenderPlayer) && world.FogObscures(a))); + var removed = actors.RemoveWhere(a => !a.IsInWorld || (!a.Owner.IsAlliedWith(self.World.RenderPlayer) && self.World.FogObscures(a))); if (removed > 0) UpdateHash(); foreach (var cg in controlGroups.Values) { // note: NOT `!a.IsInWorld`, since that would remove things that are in transports. - cg.RemoveAll(a => a.Disposed || a.Owner != world.LocalPlayer); + cg.RemoveAll(a => a.Disposed || a.Owner != self.World.LocalPlayer); } } @@ -201,7 +208,7 @@ namespace OpenRA .FirstOrDefault(); } - public List Serialize() + List IGameSaveTraitData.IssueTraitData(Actor self) { var groups = controlGroups .Where(cg => cg.Value.Any()) @@ -216,14 +223,14 @@ namespace OpenRA }; } - public void Deserialize(World world, List data) + void IGameSaveTraitData.ResolveTraitData(Actor self, List data) { var selectionNode = data.FirstOrDefault(n => n.Key == "Selection"); if (selectionNode != null) { var selected = FieldLoader.GetValue("Selection", selectionNode.Value.Value) - .Select(a => world.GetActorById(a)); - Combine(world, selected, false, false); + .Select(a => self.World.GetActorById(a)); + Combine(self.World, selected, false, false); } var groupsNode = data.FirstOrDefault(n => n.Key == "Groups"); @@ -232,7 +239,7 @@ namespace OpenRA foreach (var n in groupsNode.Value.Nodes) { var group = FieldLoader.GetValue(n.Key, n.Value.Value) - .Select(a => world.GetActorById(a)); + .Select(a => self.World.GetActorById(a)); controlGroups[int.Parse(n.Key)].AddRange(group); } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/CycleBasesHotkeyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/CycleBasesHotkeyLogic.cs index 1d9f892446..d9a5b92c55 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/CycleBasesHotkeyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/CycleBasesHotkeyLogic.cs @@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic.Ingame public class CycleBasesHotkeyLogic : SingleHotkeyBaseLogic { readonly Viewport viewport; - readonly Selection selection; + readonly ISelection selection; readonly World world; [ObjectCreator.UseCtor] diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/CycleProductionActorsHotkeyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/CycleProductionActorsHotkeyLogic.cs index 39cd78e237..358e724770 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/CycleProductionActorsHotkeyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/CycleProductionActorsHotkeyLogic.cs @@ -14,6 +14,7 @@ using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Lint; using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; using OpenRA.Widgets; namespace OpenRA.Mods.Common.Widgets.Logic.Ingame @@ -22,7 +23,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic.Ingame public class CycleProductionActorsHotkeyLogic : SingleHotkeyBaseLogic { readonly Viewport viewport; - readonly Selection selection; + readonly ISelection selection; readonly World world; readonly string clickSound = ChromeMetrics.Get("ClickSound"); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/JumpToSelectedActorsHotkeyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/JumpToSelectedActorsHotkeyLogic.cs index c662939ee8..060ac1ea16 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/JumpToSelectedActorsHotkeyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/JumpToSelectedActorsHotkeyLogic.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using OpenRA.Graphics; using OpenRA.Mods.Common.Lint; +using OpenRA.Traits; using OpenRA.Widgets; namespace OpenRA.Mods.Common.Widgets.Logic.Ingame @@ -20,7 +21,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic.Ingame public class JumpToSelectedActorsHotkeyLogic : SingleHotkeyBaseLogic { readonly Viewport viewport; - readonly Selection selection; + readonly ISelection selection; [ObjectCreator.UseCtor] public JumpToSelectedActorsHotkeyLogic(Widget widget, ModData modData, WorldRenderer worldRenderer, World world, Dictionary logicArgs) diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/RemoveFromControlGroupHotkeyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/RemoveFromControlGroupHotkeyLogic.cs index 0a79790855..02c69749cc 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/RemoveFromControlGroupHotkeyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/RemoveFromControlGroupHotkeyLogic.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Lint; +using OpenRA.Traits; using OpenRA.Widgets; namespace OpenRA.Mods.Common.Widgets.Logic.Ingame @@ -19,7 +20,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic.Ingame [ChromeLogicArgsHotkeys("RemoveFromControlGroupKey")] public class RemoveFromControlGroupHotkeyLogic : SingleHotkeyBaseLogic { - readonly Selection selection; + readonly ISelection selection; readonly World world; [ObjectCreator.UseCtor] diff --git a/mods/cnc/rules/world.yaml b/mods/cnc/rules/world.yaml index cdf42bb53c..ad35418cc0 100644 --- a/mods/cnc/rules/world.yaml +++ b/mods/cnc/rules/world.yaml @@ -3,6 +3,7 @@ Inherits: ^Palettes ScreenMap: ActorMap: + Selection: MusicPlaylist: VictoryMusic: win1 DefeatMusic: nod_map1 diff --git a/mods/d2k/rules/world.yaml b/mods/d2k/rules/world.yaml index ed23d06693..c8dc6777c2 100644 --- a/mods/d2k/rules/world.yaml +++ b/mods/d2k/rules/world.yaml @@ -3,6 +3,7 @@ AlwaysVisible: ScreenMap: ActorMap: + Selection: MusicPlaylist: VictoryMusic: score DefeatMusic: score diff --git a/mods/ra/rules/world.yaml b/mods/ra/rules/world.yaml index 320f7b94bb..d80e49d61d 100644 --- a/mods/ra/rules/world.yaml +++ b/mods/ra/rules/world.yaml @@ -3,6 +3,7 @@ AlwaysVisible: ActorMap: ScreenMap: + Selection: MusicPlaylist: VictoryMusic: score DefeatMusic: map diff --git a/mods/ts/rules/world.yaml b/mods/ts/rules/world.yaml index 20ba6a273f..21107baf96 100644 --- a/mods/ts/rules/world.yaml +++ b/mods/ts/rules/world.yaml @@ -3,6 +3,7 @@ AlwaysVisible: ScreenMap: ActorMap: + Selection: MusicPlaylist: VictoryMusic: score DefeatMusic: maps