From 1c0aa246408ffd577ec74ed5d98dfa5924564796 Mon Sep 17 00:00:00 2001 From: teinarss Date: Thu, 5 Jul 2018 22:07:30 +0000 Subject: [PATCH] Added a player action dropdown. Adds options for: - handling kick - transferring admin - move to spectator --- .../ServerTraits/LobbyCommands.cs | 61 +++++++++++++ .../Widgets/Logic/Lobby/LobbyLogic.cs | 29 +++--- .../Widgets/Logic/Lobby/LobbyUtils.cs | 91 ++++++++++++++++--- mods/cnc/chrome/dialogs.yaml | 17 ++++ mods/cnc/chrome/lobby-players.yaml | 30 +++--- mods/common/chrome/dropdowns.yaml | 16 ++++ mods/common/chrome/lobby-players.yaml | 28 +++--- 7 files changed, 212 insertions(+), 60 deletions(-) diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index 7fba15b1ed..94e2a2df29 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -37,6 +37,8 @@ namespace OpenRA.Mods.Common.Server { "option", Option }, { "assignteams", AssignTeams }, { "kick", Kick }, + { "make_admin", MakeAdmin }, + { "make_spectator", MakeSpectator }, { "name", Name }, { "faction", Faction }, { "team", Team }, @@ -601,6 +603,65 @@ namespace OpenRA.Mods.Common.Server return true; } + static bool MakeAdmin(S server, Connection conn, Session.Client client, string s) + { + if (!client.IsAdmin) + { + server.SendOrderTo(conn, "Message", "Only admins can transfer admin to another player."); + return true; + } + + int newAdminId; + Exts.TryParseIntegerInvariant(s, out newAdminId); + var newAdminConn = server.Conns.SingleOrDefault(c => server.GetClient(c) != null && server.GetClient(c).Index == newAdminId); + + if (newAdminConn == null) + { + server.SendOrderTo(conn, "Message", "No-one in that slot."); + return true; + } + + var newAdminClient = server.GetClient(newAdminConn); + client.IsAdmin = false; + newAdminClient.IsAdmin = true; + server.SendMessage("{0} is now the admin.".F(newAdminClient.Name)); + Log.Write("server", "{0} is now the admin.".F(newAdminClient.Name)); + server.SyncLobbyClients(); + + return true; + } + + static bool MakeSpectator(S server, Connection conn, Session.Client client, string s) + { + if (!client.IsAdmin) + { + server.SendOrderTo(conn, "Message", "Only the host can move players to spectators."); + return true; + } + + int targetId; + Exts.TryParseIntegerInvariant(s, out targetId); + var targetConn = server.Conns.SingleOrDefault(c => server.GetClient(c) != null && server.GetClient(c).Index == targetId); + + if (targetConn == null) + { + server.SendOrderTo(conn, "Message", "No-one in that slot."); + return true; + } + + var targetClient = server.GetClient(targetConn); + targetClient.Slot = null; + targetClient.SpawnPoint = 0; + targetClient.Team = 0; + targetClient.Color = HSLColor.FromRGB(255, 255, 255); + server.SendMessage("{0} moved {1} to spectators.".F(client.Name, targetClient.Name)); + Log.Write("server", "{0} moved {1} to spectators.".F(client.Name, targetClient.Name)); + server.SyncLobbyClients(); + CheckAutoStart(server); + + return true; + } + static bool Name(S server, Connection conn, Session.Client client, string s) { var sanitizedName = Settings.SanitizedPlayerName(s); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs index 4a5e76a20e..77872490f7 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs @@ -594,18 +594,19 @@ namespace OpenRA.Mods.Common.Widgets.Logic template = nonEditablePlayerTemplate.Clone(); LobbyUtils.SetupClientWidget(template, client, orderManager, client.Bot == null); - LobbyUtils.SetupNameWidget(template, slot, client); - LobbyUtils.SetupKickWidget(template, slot, client, orderManager, lobby, - () => panel = PanelType.Kick, () => panel = PanelType.Players); LobbyUtils.SetupColorWidget(template, slot, client); LobbyUtils.SetupFactionWidget(template, slot, client, factions); + if (isHost) { LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, map); LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, map); + LobbyUtils.SetupPlayerActionWidget(template, slot, client, orderManager, lobby, + () => panel = PanelType.Kick, () => panel = PanelType.Players); } else { + LobbyUtils.SetupNameWidget(template, slot, client); LobbyUtils.SetupTeamWidget(template, slot, client); LobbyUtils.SetupSpawnWidget(template, slot, client); } @@ -650,9 +651,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (template == null || template.Id != nonEditableSpectatorTemplate.Id) template = nonEditableSpectatorTemplate.Clone(); - LobbyUtils.SetupNameWidget(template, null, client); - LobbyUtils.SetupKickWidget(template, null, client, orderManager, lobby, - () => panel = PanelType.Kick, () => panel = PanelType.Players); + if (isHost) + LobbyUtils.SetupPlayerActionWidget(template, null, client, orderManager, lobby, + () => panel = PanelType.Kick, () => panel = PanelType.Players); + else + LobbyUtils.SetupNameWidget(template, null, client); if (client.IsAdmin) LobbyUtils.SetupReadyWidget(template, null, client); @@ -708,13 +711,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic Ui.CloseWindow(); onStart(); } - - class DropDownOption - { - public string Title; - public Func IsSelected; - public Action OnClick; - } } public class LobbyFaction @@ -724,4 +720,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic public string Description; public string Side; } + + class DropDownOption + { + public string Title; + public Func IsSelected = () => false; + public Action OnClick; + } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs index ac014c4d60..e90cade660 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs @@ -73,6 +73,60 @@ namespace OpenRA.Mods.Common.Widgets.Logic dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 167, options, setupItem); } + public static void ShowPlayerActionDropDown(DropDownButtonWidget dropdown, Session.Slot slot, + Session.Client c, OrderManager orderManager, Widget lobby, Action before, Action after) + { + Action okPressed = tempBan => { orderManager.IssueOrder(Order.Command("kick {0} {1}".F(c.Index, tempBan))); after(); }; + var onClick = new Action(() => + { + before(); + + Game.LoadWidget(null, "KICK_CLIENT_DIALOG", lobby.Get("TOP_PANELS_ROOT"), new WidgetArgs + { + { "clientName", c.Name }, + { "okPressed", okPressed }, + { "cancelPressed", after } + }); + }); + + var options = new List + { + new DropDownOption + { + Title = "Kick", + OnClick = onClick + }, + }; + + if (orderManager.LobbyInfo.GlobalSettings.Dedicated) + { + options.Add(new DropDownOption + { + Title = "Transfer Admin", + OnClick = () => orderManager.IssueOrder(Order.Command("make_admin {0}".F(c.Index))) + }); + } + + if (!c.IsObserver && orderManager.LobbyInfo.GlobalSettings.AllowSpectators) + { + options.Add(new DropDownOption + { + Title = "Move to Spectator", + OnClick = () => orderManager.IssueOrder(Order.Command("make_spectator {0}".F(c.Index))) + }); + } + + Func setupItem = (o, itemTemplate) => + { + var item = ScrollItemWidget.Setup(itemTemplate, o.IsSelected, o.OnClick); + var labelWidget = item.Get("LABEL"); + labelWidget.GetText = () => o.Title; + return item; + }; + + dropdown.ShowDropDown("PLAYERACTION_DROPDOWN_TEMPLATE", 167, options, setupItem); + } + public static void ShowTeamDropDown(DropDownButtonWidget dropdown, Session.Client client, OrderManager orderManager, int teamCount) { @@ -316,6 +370,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic public static void SetupNameWidget(Widget parent, Session.Slot s, Session.Client c) { var name = parent.Get("NAME"); + name.IsVisible = () => true; var font = Game.Renderer.Fonts[name.Font]; var label = WidgetUtils.TruncateText(c.Name, name.Bounds.Width, font); name.GetText = () => label; @@ -343,23 +398,16 @@ namespace OpenRA.Mods.Common.Widgets.Logic HideChildWidget(parent, "SLOT_OPTIONS"); } - public static void SetupKickWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, Widget lobby, Action before, Action after) + public static void SetupPlayerActionWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, Widget lobby, Action before, Action after) { - var button = parent.Get("KICK"); - button.IsVisible = () => Game.IsHost && c.Index != orderManager.LocalClient.Index; - button.IsDisabled = () => orderManager.LocalClient.IsReady; - Action okPressed = tempBan => { orderManager.IssueOrder(Order.Command("kick {0} {1}".F(c.Index, tempBan))); after(); }; - button.OnClick = () => - { - before(); + var slot = parent.Get("PLAYER_ACTION"); + slot.IsVisible = () => Game.IsHost && c.Index != orderManager.LocalClient.Index; + slot.IsDisabled = () => orderManager.LocalClient.IsReady; + slot.GetText = () => c != null ? c.Name : string.Empty; + slot.OnMouseDown = _ => ShowPlayerActionDropDown(slot, s, c, orderManager, lobby, before, after); - Game.LoadWidget(null, "KICK_CLIENT_DIALOG", lobby.Get("TOP_PANELS_ROOT"), new WidgetArgs - { - { "clientName", c.Name }, - { "okPressed", okPressed }, - { "cancelPressed", after } - }); - }; + // Ensure Name selector (if present) is hidden + HideChildWidget(parent, "NAME"); } public static void SetupKickSpectatorsWidget(Widget parent, OrderManager orderManager, Widget lobby, Action before, Action after, bool skirmishMode) @@ -578,4 +626,17 @@ namespace OpenRA.Mods.Common.Widgets.Logic widget.IsVisible = () => false; } } + + class ShowPlayerActionDropDownOption + { + public Action Click { get; set; } + public string Title; + public Func Selected = () => false; + + public ShowPlayerActionDropDownOption(string title, Action click) + { + Click = click; + Title = title; + } + } } diff --git a/mods/cnc/chrome/dialogs.yaml b/mods/cnc/chrome/dialogs.yaml index ad88a45018..66c4d5f521 100644 --- a/mods/cnc/chrome/dialogs.yaml +++ b/mods/cnc/chrome/dialogs.yaml @@ -26,6 +26,23 @@ ScrollPanel@LABEL_DROPDOWN_TEMPLATE: Width: PARENT_RIGHT - 20 Height: 25 +ScrollPanel@PLAYERACTION_DROPDOWN_TEMPLATE: + Width: DROPDOWN_WIDTH + Background: panel-black + Children: + ScrollItem@TEMPLATE: + Width: PARENT_RIGHT - 27 + Height: 25 + X: 2 + Y: 0 + Visible: false + Children: + Label@LABEL: + X: 10 + Width: PARENT_RIGHT - 20 + Height: 25 + Align: Left + ScrollPanel@FACTION_DROPDOWN_TEMPLATE: Width: DROPDOWN_WIDTH Background: panel-black diff --git a/mods/cnc/chrome/lobby-players.yaml b/mods/cnc/chrome/lobby-players.yaml index 8a830baa8b..3f42ef6765 100644 --- a/mods/cnc/chrome/lobby-players.yaml +++ b/mods/cnc/chrome/lobby-players.yaml @@ -185,16 +185,13 @@ Container@LOBBY_PLAYER_BIN: Y: 0 - 1 Width: 180 Height: 25 - Button@KICK: - X: 180 - Width: 25 + DropDownButton@PLAYER_ACTION: + X: 15 + Width: 190 Height: 25 - Children: - Image: - ImageCollection: lobby-bits - ImageName: kick - X: 7 - Y: 7 + Font: Regular + Visible: false + Align: Left ColorBlock@COLORBLOCK: X: 215 Y: 6 @@ -358,16 +355,13 @@ Container@LOBBY_PLAYER_BIN: Y: 0 - 1 Width: 180 Height: 25 - Button@KICK: - X: 180 - Width: 25 + DropDownButton@PLAYER_ACTION: + X: 15 + Width: 190 Height: 25 - Children: - Image: - ImageCollection: lobby-bits - ImageName: kick - X: 7 - Y: 7 + Font: Regular + Visible: false + Align: Left Label@SPECTATOR: X: 210 Width: 341 diff --git a/mods/common/chrome/dropdowns.yaml b/mods/common/chrome/dropdowns.yaml index c24dbe626d..a382dd7783 100644 --- a/mods/common/chrome/dropdowns.yaml +++ b/mods/common/chrome/dropdowns.yaml @@ -26,6 +26,22 @@ ScrollPanel@LABEL_DROPDOWN_TEMPLATE: Width: PARENT_RIGHT - 20 Height: 25 +ScrollPanel@PLAYERACTION_DROPDOWN_TEMPLATE: + Width: DROPDOWN_WIDTH + Children: + ScrollItem@TEMPLATE: + Width: PARENT_RIGHT - 27 + Height: 25 + X: 2 + Y: 0 + Visible: false + Children: + Label@LABEL: + X: 10 + Width: PARENT_RIGHT - 20 + Height: 25 + Align: Left + ScrollPanel@TEAM_DROPDOWN_TEMPLATE: Width: DROPDOWN_WIDTH Children: diff --git a/mods/common/chrome/lobby-players.yaml b/mods/common/chrome/lobby-players.yaml index 9f5dd78a16..a191749c7c 100644 --- a/mods/common/chrome/lobby-players.yaml +++ b/mods/common/chrome/lobby-players.yaml @@ -181,13 +181,13 @@ Container@LOBBY_PLAYER_BIN: Width: 165 Height: 25 Text: Name - Button@KICK: - X: 155 - Y: 2 - Width: 25 - Height: 23 - Text: X - Font: Bold + DropDownButton@PLAYER_ACTION: + X: 15 + Width: 165 + Height: 25 + Font: Regular + Visible: false + Align: Left ColorBlock@COLORBLOCK: X: 195 Y: 6 @@ -347,13 +347,13 @@ Container@LOBBY_PLAYER_BIN: X: 20 Y: 0 - 1 Text: Name - Button@KICK: - X: 155 - Y: 2 - Width: 25 - Height: 23 - Text: X - Font: Bold + DropDownButton@PLAYER_ACTION: + X: 15 + Width: 165 + Height: 25 + Font: Regular + Visible: false + Align: Left Label@SPECTATOR: X: 190 Width: 326