From 24f166595f10aa40eef4189e6bf1d18049592413 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 19 May 2016 14:39:15 +0100 Subject: [PATCH] Convert lobby dropdowns to new options backend. --- OpenRA.Game/Network/Session.cs | 3 - OpenRA.Game/Traits/Player/PlayerResources.cs | 17 +- .../ServerTraits/LobbyCommands.cs | 114 ------------ .../ServerTraits/LobbySettingsNotification.cs | 14 -- .../Traits/Player/ProvidesTechPrerequisite.cs | 3 +- OpenRA.Mods.Common/Traits/World/MapOptions.cs | 15 +- .../Traits/World/SpawnMPUnits.cs | 30 +++- .../Widgets/Logic/Lobby/LobbyLogic.cs | 170 +++++++----------- 8 files changed, 118 insertions(+), 248 deletions(-) diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 0433e2a2da..a7beed5d53 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -193,9 +193,6 @@ namespace OpenRA.Network public int RandomSeed = 0; public bool AllowSpectators = true; public string Difficulty; - public int StartingCash = 5000; - public string TechLevel; - public string StartingUnitsClass; public string GameSpeedType = "default"; public bool AllowVersionMismatch; public string GameUid; diff --git a/OpenRA.Game/Traits/Player/PlayerResources.cs b/OpenRA.Game/Traits/Player/PlayerResources.cs index 1b32342a2c..d8801772f3 100644 --- a/OpenRA.Game/Traits/Player/PlayerResources.cs +++ b/OpenRA.Game/Traits/Player/PlayerResources.cs @@ -10,11 +10,12 @@ #endregion using System; +using System.Collections.Generic; using System.Linq; namespace OpenRA.Traits { - public class PlayerResourcesInfo : ITraitInfo + public class PlayerResourcesInfo : ITraitInfo, ILobbyOptions { [Desc("Starting cash options that are available in the lobby options.")] public readonly int[] SelectableCash = { 2500, 5000, 10000, 20000 }; @@ -31,6 +32,14 @@ namespace OpenRA.Traits [Desc("Delay (in ticks) during which warnings will be muted.")] public readonly int InsufficientFundsNotificationDelay = 750; + IEnumerable ILobbyOptions.LobbyOptions(Ruleset rules) + { + var startingCash = SelectableCash.ToDictionary(c => c.ToString(), c => "$" + c.ToString()); + + if (startingCash.Any()) + yield return new LobbyOption("startingcash", "Starting Cash", new ReadOnlyDictionary(startingCash), DefaultCash.ToString(), DefaultCashLocked); + } + public object Create(ActorInitializer init) { return new PlayerResources(init.Self, this); } } @@ -46,7 +55,11 @@ namespace OpenRA.Traits this.info = info; owner = self.Owner; - Cash = self.World.LobbyInfo.GlobalSettings.StartingCash; + var startingCash = self.World.LobbyInfo.GlobalSettings + .OptionOrDefault("startingcash", info.DefaultCash.ToString()); + + if (!int.TryParse(startingCash, out Cash)) + Cash = info.DefaultCash; } [Sync] public int Cash; diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index dd9dcc260e..7375ffd4de 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -539,110 +539,6 @@ namespace OpenRA.Mods.Common.Server return true; } }, - { "startingunits", - s => - { - if (!client.IsAdmin) - { - server.SendOrderTo(conn, "Message", "Only the host can set that option."); - return true; - } - - var startingUnits = server.Map.Rules.Actors["world"].TraitInfoOrDefault(); - if (startingUnits == null || startingUnits.Locked) - { - server.SendOrderTo(conn, "Message", "Map has disabled start unit configuration."); - return true; - } - - var startUnitsInfo = server.Map.Rules.Actors["world"].TraitInfos(); - var selectedClass = startUnitsInfo.Where(u => u.Class == s).FirstOrDefault(); - if (selectedClass == null) - { - server.SendOrderTo(conn, "Message", "Invalid starting units option selected: {0}".F(s)); - server.SendOrderTo(conn, "Message", "Supported values: {0}".F(startUnitsInfo.Select(su => su.ClassName).JoinWith(", "))); - return true; - } - - if (server.LobbyInfo.GlobalSettings.StartingUnitsClass == selectedClass.Class) - return true; - - server.LobbyInfo.GlobalSettings.StartingUnitsClass = selectedClass.Class; - server.SyncLobbyGlobalSettings(); - server.SendMessage("{0} changed Starting Units to {1}.".F(client.Name, selectedClass.ClassName)); - - return true; - } - }, - { "startingcash", - s => - { - if (!client.IsAdmin) - { - server.SendOrderTo(conn, "Message", "Only the host can set that option."); - return true; - } - - var playerResources = server.Map.Rules.Actors["player"].TraitInfo(); - if (playerResources.DefaultCashLocked) - { - server.SendOrderTo(conn, "Message", "Map has disabled cash configuration."); - return true; - } - - var startingCashOptions = playerResources.SelectableCash; - var requestedCash = Exts.ParseIntegerInvariant(s); - if (!startingCashOptions.Contains(requestedCash)) - { - server.SendOrderTo(conn, "Message", "Invalid starting cash value selected: {0}".F(s)); - server.SendOrderTo(conn, "Message", "Supported values: {0}".F(startingCashOptions.JoinWith(", "))); - return true; - } - - if (server.LobbyInfo.GlobalSettings.StartingCash == requestedCash) - return true; - - server.LobbyInfo.GlobalSettings.StartingCash = requestedCash; - server.SyncLobbyGlobalSettings(); - server.SendMessage("{0} changed Starting Cash to ${1}.".F(client.Name, requestedCash)); - - return true; - } - }, - { "techlevel", - s => - { - if (server.LobbyInfo.GlobalSettings.TechLevel == s) - return true; - - if (!client.IsAdmin) - { - server.SendOrderTo(conn, "Message", "Only the host can set that option."); - return true; - } - - var mapOptions = server.Map.Rules.Actors["world"].TraitInfo(); - if (mapOptions.TechLevelLocked) - { - server.SendOrderTo(conn, "Message", "Map has disabled Tech configuration."); - return true; - } - - var techlevels = server.Map.Rules.Actors["player"].TraitInfos().Select(t => t.Name); - if (!techlevels.Contains(s)) - { - server.SendOrderTo(conn, "Message", "Invalid tech level selected: {0}".F(s)); - server.SendOrderTo(conn, "Message", "Supported values: {0}".F(techlevels.JoinWith(", "))); - return true; - } - - server.LobbyInfo.GlobalSettings.TechLevel = s; - server.SyncLobbyInfo(); - server.SendMessage("{0} changed Tech Level to {1}.".F(client.Name, s)); - - return true; - } - }, { "gamespeed", s => { @@ -970,16 +866,6 @@ namespace OpenRA.Mods.Common.Server state.PreferredValue = preferredValue; gs.LobbyOptions[o.Id] = state; } - - var resources = rules.Actors["player"].TraitInfo(); - gs.StartingCash = resources.DefaultCash; - - var startingUnits = rules.Actors["world"].TraitInfoOrDefault(); - gs.StartingUnitsClass = startingUnits == null ? "none" : startingUnits.StartingUnitsClass; - - var mapOptions = rules.Actors["world"].TraitInfo(); - gs.TechLevel = mapOptions.TechLevel; - gs.Difficulty = mapOptions.Difficulty ?? mapOptions.Difficulties.FirstOrDefault(); } static HSLColor SanitizePlayerColor(S server, HSLColor askedColor, int playerIndex, Connection connectionToEcho = null) diff --git a/OpenRA.Mods.Common/ServerTraits/LobbySettingsNotification.cs b/OpenRA.Mods.Common/ServerTraits/LobbySettingsNotification.cs index a096ff8f2d..1ce348c8cb 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbySettingsNotification.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbySettingsNotification.cs @@ -38,20 +38,6 @@ namespace OpenRA.Mods.Common.Server if (!defaults.LobbyOptions.TryGetValue(kv.Key, out def) || kv.Value.Value != def.Value) server.SendOrderTo(conn, "Message", options[kv.Key].Name + ": " + kv.Value.Value); } - - if (server.LobbyInfo.GlobalSettings.StartingUnitsClass != defaults.StartingUnitsClass) - { - var startUnitsInfo = server.Map.Rules.Actors["world"].TraitInfos(); - var selectedClass = startUnitsInfo.Where(u => u.Class == server.LobbyInfo.GlobalSettings.StartingUnitsClass).Select(u => u.ClassName).FirstOrDefault(); - var className = selectedClass != null ? selectedClass : server.LobbyInfo.GlobalSettings.StartingUnitsClass; - server.SendOrderTo(conn, "Message", "Starting Units: {0}".F(className)); - } - - if (server.LobbyInfo.GlobalSettings.StartingCash != defaults.StartingCash) - server.SendOrderTo(conn, "Message", "Starting Cash: ${0}".F(server.LobbyInfo.GlobalSettings.StartingCash)); - - if (server.LobbyInfo.GlobalSettings.TechLevel != defaults.TechLevel) - server.SendOrderTo(conn, "Message", "Tech Level: {0}".F(server.LobbyInfo.GlobalSettings.TechLevel)); } } } \ No newline at end of file diff --git a/OpenRA.Mods.Common/Traits/Player/ProvidesTechPrerequisite.cs b/OpenRA.Mods.Common/Traits/Player/ProvidesTechPrerequisite.cs index 96661db6b6..130061c2b8 100644 --- a/OpenRA.Mods.Common/Traits/Player/ProvidesTechPrerequisite.cs +++ b/OpenRA.Mods.Common/Traits/Player/ProvidesTechPrerequisite.cs @@ -48,7 +48,8 @@ namespace OpenRA.Mods.Common.Traits public ProvidesTechPrerequisite(ProvidesTechPrerequisiteInfo info, ActorInitializer init) { this.info = info; - enabled = info.Name == init.World.LobbyInfo.GlobalSettings.TechLevel; + var mapOptions = init.World.WorldActor.TraitOrDefault(); + enabled = mapOptions != null && mapOptions.TechLevel == info.Id; } } } diff --git a/OpenRA.Mods.Common/Traits/World/MapOptions.cs b/OpenRA.Mods.Common/Traits/World/MapOptions.cs index 2a1c059635..70cb14b88d 100644 --- a/OpenRA.Mods.Common/Traits/World/MapOptions.cs +++ b/OpenRA.Mods.Common/Traits/World/MapOptions.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Linq; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -24,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits public readonly bool ShortGameLocked = false; [Desc("Default tech level.")] - public readonly string TechLevel = "Unrestricted"; + public readonly string TechLevel = "unrestricted"; [Desc("Prevent the tech level from being changed in the lobby.")] public readonly bool TechLevelLocked = false; @@ -41,6 +42,14 @@ namespace OpenRA.Mods.Common.Traits IEnumerable ILobbyOptions.LobbyOptions(Ruleset rules) { yield return new LobbyBooleanOption("shortgame", "Short Game", ShortGameEnabled, ShortGameLocked); + + var techLevels = rules.Actors["player"].TraitInfos() + .ToDictionary(t => t.Id, t => t.Name); + + if (techLevels.Any()) + yield return new LobbyOption("techlevel", "Tech Level", + new ReadOnlyDictionary(techLevels), + TechLevel, TechLevelLocked); } public object Create(ActorInitializer init) { return new MapOptions(this); } @@ -51,6 +60,7 @@ namespace OpenRA.Mods.Common.Traits readonly MapOptionsInfo info; public bool ShortGame { get; private set; } + public string TechLevel { get; private set; } public MapOptions(MapOptionsInfo info) { @@ -61,6 +71,9 @@ namespace OpenRA.Mods.Common.Traits { ShortGame = self.World.LobbyInfo.GlobalSettings .OptionOrDefault("shortgame", info.ShortGameEnabled); + + TechLevel = self.World.LobbyInfo.GlobalSettings + .OptionOrDefault("techlevel", info.TechLevel); } } } diff --git a/OpenRA.Mods.Common/Traits/World/SpawnMPUnits.cs b/OpenRA.Mods.Common/Traits/World/SpawnMPUnits.cs index 3bbc5e5e02..8d2fa93120 100644 --- a/OpenRA.Mods.Common/Traits/World/SpawnMPUnits.cs +++ b/OpenRA.Mods.Common/Traits/World/SpawnMPUnits.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; using OpenRA.Primitives; @@ -18,25 +19,48 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Spawn base actor at the spawnpoint and support units in an annulus around the base actor. Both are defined at MPStartUnits. Attach this to the world actor.")] - public class SpawnMPUnitsInfo : TraitInfo, Requires, Requires + public class SpawnMPUnitsInfo : ITraitInfo, Requires, Requires, ILobbyOptions { public readonly string StartingUnitsClass = "none"; [Desc("Prevent the starting units option from being changed in the lobby.")] public bool Locked = false; + + IEnumerable ILobbyOptions.LobbyOptions(Ruleset rules) + { + var startingUnits = new Dictionary(); + + // Duplicate classes are defined for different race variants + foreach (var t in rules.Actors["world"].TraitInfos()) + startingUnits[t.Class] = t.ClassName; + + if (startingUnits.Any()) + yield return new LobbyOption("startingunits", "Starting Units", new ReadOnlyDictionary(startingUnits), StartingUnitsClass, Locked); + } + + public object Create(ActorInitializer init) { return new SpawnMPUnits(this); } } public class SpawnMPUnits : IWorldLoaded { + readonly SpawnMPUnitsInfo info; + + public SpawnMPUnits(SpawnMPUnitsInfo info) + { + this.info = info; + } + public void WorldLoaded(World world, WorldRenderer wr) { foreach (var s in world.WorldActor.Trait().Start) SpawnUnitsForPlayer(world, s.Key, s.Value); } - static void SpawnUnitsForPlayer(World w, Player p, CPos sp) + void SpawnUnitsForPlayer(World w, Player p, CPos sp) { - var spawnClass = p.PlayerReference.StartingUnitsClass ?? w.LobbyInfo.GlobalSettings.StartingUnitsClass; + var spawnClass = p.PlayerReference.StartingUnitsClass ?? w.LobbyInfo.GlobalSettings + .OptionOrDefault("startingunits", info.StartingUnitsClass); + var unitGroup = w.Map.Rules.Actors["world"].TraitInfos() .Where(g => g.Class == spawnClass && g.Factions != null && g.Factions.Contains(p.Faction.InternalName)) .RandomOrDefault(w.SharedRandom); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs index 65815e1de9..ccbe00a7fb 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs @@ -374,6 +374,66 @@ namespace OpenRA.Mods.Common.Widgets.Logic } } + var optionDropdowns = new Dictionary() + { + { "TECHLEVEL", "techlevel" }, + { "STARTINGUNITS", "startingunits" }, + { "STARTINGCASH", "startingcash" }, + }; + + var allOptions = new CachedTransform( + map => map.Rules.Actors["player"].TraitInfos() + .Concat(map.Rules.Actors["world"].TraitInfos()) + .SelectMany(t => t.LobbyOptions(map.Rules)) + .ToArray()); + + foreach (var kv in optionDropdowns) + { + var dropdown = optionsBin.GetOrNull(kv.Key + "_DROPDOWNBUTTON"); + if (dropdown != null) + { + var optionValue = new CachedTransform( + gs => gs.LobbyOptions[kv.Value]); + + var option = new CachedTransform( + map => allOptions.Update(map).FirstOrDefault(o => o.Id == kv.Value)); + + var getOptionLabel = new CachedTransform(id => + { + string value; + if (id == null || !option.Update(Map).Values.TryGetValue(id, out value)) + return "Not Available"; + + return value; + }); + + dropdown.GetText = () => getOptionLabel.Update(optionValue.Update(orderManager.LobbyInfo.GlobalSettings).Value); + dropdown.IsVisible = () => option.Update(Map) != null; + dropdown.IsDisabled = () => configurationDisabled() || + optionValue.Update(orderManager.LobbyInfo.GlobalSettings).Locked; + + dropdown.OnMouseDown = _ => + { + Func, ScrollItemWidget, ScrollItemWidget> setupItem = (c, template) => + { + Func isSelected = () => optionValue.Update(orderManager.LobbyInfo.GlobalSettings).Value == c.Key; + Action onClick = () => orderManager.IssueOrder(Order.Command("option {0} {1}".F(kv.Value, c.Key))); + + var item = ScrollItemWidget.Setup(template, isSelected, onClick); + item.Get("LABEL").GetText = () => c.Value; + return item; + }; + + var options = option.Update(Map).Values; + dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem); + }; + + var label = optionsBin.GetOrNull(kv.Key + "_DESC"); + if (label != null) + label.IsVisible = () => option.Update(Map) != null; + } + } + var difficulty = optionsBin.GetOrNull("DIFFICULTY_DROPDOWNBUTTON"); if (difficulty != null) { @@ -403,116 +463,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic optionsBin.Get("DIFFICULTY_DESC").IsVisible = difficulty.IsVisible; } - var startingUnits = optionsBin.GetOrNull("STARTINGUNITS_DROPDOWNBUTTON"); - if (startingUnits != null) - { - var startUnitsInfos = new CachedTransform>( - map => map.Rules.Actors["world"].TraitInfos()); - - var startUnitsLocked = new CachedTransform(map => - { - var spawnUnitsInfo = map.Rules.Actors["world"].TraitInfoOrDefault(); - return spawnUnitsInfo == null || spawnUnitsInfo.Locked; - }); - - Func className = c => - { - var selectedClass = startUnitsInfos.Update(Map).Where(s => s.Class == c).Select(u => u.ClassName).FirstOrDefault(); - return selectedClass != null ? selectedClass : c; - }; - - startingUnits.IsDisabled = () => configurationDisabled() || startUnitsLocked.Update(Map); - startingUnits.GetText = () => !Map.RulesLoaded || startUnitsLocked.Update(Map) ? - "Not Available" : className(orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass); - startingUnits.OnMouseDown = _ => - { - var classes = startUnitsInfos.Update(Map).Select(a => a.Class).Distinct(); - var options = classes.Select(c => new DropDownOption - { - Title = className(c), - IsSelected = () => orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass == c, - OnClick = () => orderManager.IssueOrder(Order.Command("startingunits {0}".F(c))) - }); - - Func setupItem = (option, template) => - { - var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick); - item.Get("LABEL").GetText = () => option.Title; - return item; - }; - - startingUnits.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem); - }; - - optionsBin.Get("STARTINGUNITS_DESC").IsVisible = startingUnits.IsVisible; - } - - var startingCash = optionsBin.GetOrNull("STARTINGCASH_DROPDOWNBUTTON"); - if (startingCash != null) - { - var playerResources = new CachedTransform( - map => map.Rules.Actors["player"].TraitInfo()); - - startingCash.IsDisabled = () => configurationDisabled() || playerResources.Update(Map).DefaultCashLocked; - startingCash.GetText = () => !Map.RulesLoaded || playerResources.Update(Map).DefaultCashLocked ? - "Not Available" : "${0}".F(orderManager.LobbyInfo.GlobalSettings.StartingCash); - startingCash.OnMouseDown = _ => - { - var options = playerResources.Update(Map).SelectableCash.Select(c => new DropDownOption - { - Title = "${0}".F(c), - IsSelected = () => orderManager.LobbyInfo.GlobalSettings.StartingCash == c, - OnClick = () => orderManager.IssueOrder(Order.Command("startingcash {0}".F(c))) - }); - - Func setupItem = (option, template) => - { - var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick); - item.Get("LABEL").GetText = () => option.Title; - return item; - }; - - startingCash.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem); - }; - } - - var techLevel = optionsBin.GetOrNull("TECHLEVEL_DROPDOWNBUTTON"); - if (techLevel != null) - { - var mapOptions = new CachedTransform( - map => map.Rules.Actors["world"].TraitInfo()); - - var techLevels = new CachedTransform>( - map => map.Rules.Actors["player"].TraitInfos().ToList()); - - techLevel.IsVisible = () => Map.RulesLoaded && techLevels.Update(Map).Any(); - var techLevelDescription = optionsBin.GetOrNull("TECHLEVEL_DESC"); - if (techLevelDescription != null) - techLevelDescription.IsVisible = techLevel.IsVisible; - - techLevel.IsDisabled = () => configurationDisabled() || mapOptions.Update(Map).TechLevelLocked; - techLevel.GetText = () => !Map.RulesLoaded || mapOptions.Update(Map).TechLevelLocked ? - "Not Available" : "{0}".F(orderManager.LobbyInfo.GlobalSettings.TechLevel); - techLevel.OnMouseDown = _ => - { - var options = techLevels.Update(Map).Select(c => new DropDownOption - { - Title = "{0}".F(c.Name), - IsSelected = () => orderManager.LobbyInfo.GlobalSettings.TechLevel == c.Name, - OnClick = () => orderManager.IssueOrder(Order.Command("techlevel {0}".F(c.Name))) - }); - - Func setupItem = (option, template) => - { - var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick); - item.Get("LABEL").GetText = () => option.Title; - return item; - }; - - techLevel.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem); - }; - } - var gameSpeed = optionsBin.GetOrNull("GAMESPEED_DROPDOWNBUTTON"); if (gameSpeed != null) {