VoteCallMenu.xaml.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. using System.Linq;
  2. using System.Numerics;
  3. using Content.Client.Gameplay;
  4. using Content.Client.Stylesheets;
  5. using Content.Shared.Administration;
  6. using Content.Shared.CCVar;
  7. using Content.Shared.Ghost;
  8. using Content.Shared.Voting;
  9. using JetBrains.Annotations;
  10. using Robust.Client.AutoGenerated;
  11. using Robust.Client.Console;
  12. using Robust.Client.State;
  13. using Robust.Client.UserInterface.Controls;
  14. using Robust.Client.UserInterface.CustomControls;
  15. using Robust.Client.UserInterface.XAML;
  16. using Robust.Shared.Configuration;
  17. using Robust.Shared.Console;
  18. using Robust.Shared.Network;
  19. using Robust.Shared.Timing;
  20. namespace Content.Client.Voting.UI
  21. {
  22. [GenerateTypedNameReferences]
  23. public sealed partial class VoteCallMenu : BaseWindow
  24. {
  25. [Dependency] private readonly IClientConsoleHost _consoleHost = default!;
  26. [Dependency] private readonly IVoteManager _voteManager = default!;
  27. [Dependency] private readonly IGameTiming _gameTiming = default!;
  28. [Dependency] private readonly IClientNetManager _netManager = default!;
  29. [Dependency] private readonly IEntityManager _entityManager = default!;
  30. [Dependency] private readonly IEntityNetworkManager _entNetManager = default!;
  31. [Dependency] private readonly IConfigurationManager _cfg = default!;
  32. [Dependency] private readonly IStateManager _state = default!;
  33. private VotingSystem _votingSystem;
  34. public StandardVoteType Type;
  35. public Dictionary<StandardVoteType, CreateVoteOption> AvailableVoteOptions = new Dictionary<StandardVoteType, CreateVoteOption>()
  36. {
  37. { StandardVoteType.Restart, new CreateVoteOption("ui-vote-type-restart", new(), false, null) },
  38. { StandardVoteType.Preset, new CreateVoteOption("ui-vote-type-gamemode", new(), false, null) },
  39. { StandardVoteType.Map, new CreateVoteOption("ui-vote-type-map", new(), false, null) },
  40. { StandardVoteType.Votekick, new CreateVoteOption("ui-vote-type-votekick", new(), true, 0) }
  41. };
  42. public Dictionary<string, string> VotekickReasons = new Dictionary<string, string>()
  43. {
  44. { VotekickReasonType.Raiding.ToString(), Loc.GetString("ui-vote-votekick-type-raiding") },
  45. { VotekickReasonType.Cheating.ToString(), Loc.GetString("ui-vote-votekick-type-cheating") },
  46. { VotekickReasonType.Spam.ToString(), Loc.GetString("ui-vote-votekick-type-spamming") }
  47. };
  48. public Dictionary<NetUserId, (NetEntity, string)> PlayerList = new();
  49. public OptionButton? _followDropdown = null;
  50. public bool IsAllowedVotekick = false;
  51. public VoteCallMenu()
  52. {
  53. IoCManager.InjectDependencies(this);
  54. RobustXamlLoader.Load(this);
  55. _votingSystem = _entityManager.System<VotingSystem>();
  56. Stylesheet = IoCManager.Resolve<IStylesheetManager>().SheetSpace;
  57. CloseButton.OnPressed += _ => Close();
  58. VoteNotTrustedLabel.Text = Loc.GetString("ui-vote-trusted-users-notice", ("timeReq", _cfg.GetCVar(CCVars.VotekickEligibleVoterDeathtime)));
  59. foreach (StandardVoteType voteType in Enum.GetValues<StandardVoteType>())
  60. {
  61. var option = AvailableVoteOptions[voteType];
  62. VoteTypeButton.AddItem(Loc.GetString(option.Name), (int)voteType);
  63. }
  64. _state.OnStateChanged += OnStateChanged;
  65. VoteTypeButton.OnItemSelected += VoteTypeSelected;
  66. CreateButton.OnPressed += CreatePressed;
  67. FollowButton.OnPressed += FollowSelected;
  68. }
  69. protected override void Opened()
  70. {
  71. base.Opened();
  72. _netManager.ClientSendMessage(new MsgVoteMenu());
  73. _voteManager.CanCallVoteChanged += CanCallVoteChanged;
  74. _votingSystem.VotePlayerListResponse += UpdateVotePlayerList;
  75. _votingSystem.RequestVotePlayerList();
  76. }
  77. public override void Close()
  78. {
  79. base.Close();
  80. _voteManager.CanCallVoteChanged -= CanCallVoteChanged;
  81. _votingSystem.VotePlayerListResponse -= UpdateVotePlayerList;
  82. }
  83. protected override void FrameUpdate(FrameEventArgs args)
  84. {
  85. base.FrameUpdate(args);
  86. UpdateVoteTimeout();
  87. }
  88. private void OnStateChanged(StateChangedEventArgs obj)
  89. {
  90. if (obj.NewState is not GameplayState)
  91. return;
  92. Close();
  93. }
  94. private void CanCallVoteChanged(bool obj)
  95. {
  96. if (!obj)
  97. Close();
  98. }
  99. private void UpdateVotePlayerList(VotePlayerListResponseEvent msg)
  100. {
  101. Dictionary<string, string> optionsList = new();
  102. Dictionary<NetUserId, (NetEntity, string)> playerList = new();
  103. foreach ((NetUserId, NetEntity, string) player in msg.Players)
  104. {
  105. optionsList.Add(player.Item1.ToString(), player.Item3);
  106. playerList.Add(player.Item1, (player.Item2, player.Item3));
  107. }
  108. if (optionsList.Count == 0)
  109. optionsList.Add(" ", " ");
  110. PlayerList = playerList;
  111. IsAllowedVotekick = !msg.Denied;
  112. var updatedDropdownOption = AvailableVoteOptions[StandardVoteType.Votekick];
  113. updatedDropdownOption.Dropdowns = new List<Dictionary<string, string>>() { optionsList, VotekickReasons };
  114. AvailableVoteOptions[StandardVoteType.Votekick] = updatedDropdownOption;
  115. }
  116. private void CreatePressed(BaseButton.ButtonEventArgs obj)
  117. {
  118. var typeId = VoteTypeButton.SelectedId;
  119. var voteType = AvailableVoteOptions[(StandardVoteType)typeId];
  120. var commandArgs = "";
  121. if (voteType.Dropdowns == null || voteType.Dropdowns.Count == 0)
  122. {
  123. _consoleHost.LocalShell.RemoteExecuteCommand($"createvote {((StandardVoteType)typeId).ToString()}");
  124. }
  125. else
  126. {
  127. int i = 0;
  128. foreach(var dropdowns in VoteOptionsButtonContainer.Children)
  129. {
  130. if (dropdowns is OptionButton optionButton && AvailableVoteOptions[(StandardVoteType)typeId].Dropdowns != null)
  131. {
  132. commandArgs += AvailableVoteOptions[(StandardVoteType)typeId].Dropdowns[i].ElementAt(optionButton.SelectedId).Key + " ";
  133. i++;
  134. }
  135. }
  136. _consoleHost.LocalShell.RemoteExecuteCommand($"createvote {((StandardVoteType)typeId).ToString()} {commandArgs}");
  137. }
  138. Close();
  139. }
  140. private void UpdateVoteTimeout()
  141. {
  142. var typeKey = (StandardVoteType)VoteTypeButton.SelectedId;
  143. var isAvailable = _voteManager.CanCallStandardVote(typeKey, out var timeout);
  144. if (typeKey == StandardVoteType.Votekick && !IsAllowedVotekick)
  145. {
  146. CreateButton.Disabled = true;
  147. }
  148. else
  149. {
  150. CreateButton.Disabled = !isAvailable;
  151. }
  152. VoteTypeTimeoutLabel.Visible = !isAvailable;
  153. if (!isAvailable)
  154. {
  155. if (timeout == TimeSpan.Zero)
  156. {
  157. VoteTypeTimeoutLabel.Text = Loc.GetString("ui-vote-type-not-available");
  158. }
  159. else
  160. {
  161. var remaining = timeout - _gameTiming.RealTime;
  162. VoteTypeTimeoutLabel.Text = Loc.GetString("ui-vote-type-timeout", ("remaining", remaining.ToString("mm\\:ss")));
  163. }
  164. }
  165. }
  166. private static void ButtonSelected(OptionButton.ItemSelectedEventArgs obj)
  167. {
  168. obj.Button.SelectId(obj.Id);
  169. }
  170. private void FollowSelected(Button.ButtonEventArgs obj)
  171. {
  172. if (_followDropdown == null)
  173. return;
  174. if (_followDropdown.SelectedId >= PlayerList.Count)
  175. return;
  176. var netEntity = PlayerList.ElementAt(_followDropdown.SelectedId).Value.Item1;
  177. var msg = new GhostWarpToTargetRequestEvent(netEntity);
  178. _entNetManager.SendSystemNetworkMessage(msg);
  179. }
  180. private void VoteTypeSelected(OptionButton.ItemSelectedEventArgs obj)
  181. {
  182. VoteTypeButton.SelectId(obj.Id);
  183. VoteNotTrustedLabel.Visible = false;
  184. if ((StandardVoteType)obj.Id == StandardVoteType.Votekick)
  185. {
  186. if (!IsAllowedVotekick)
  187. {
  188. VoteNotTrustedLabel.Visible = true;
  189. var updatedDropdownOption = AvailableVoteOptions[StandardVoteType.Votekick];
  190. updatedDropdownOption.Dropdowns = new List<Dictionary<string, string>>();
  191. AvailableVoteOptions[StandardVoteType.Votekick] = updatedDropdownOption;
  192. }
  193. else
  194. {
  195. _votingSystem.RequestVotePlayerList();
  196. }
  197. }
  198. VoteWarningLabel.Visible = AvailableVoteOptions[(StandardVoteType)obj.Id].EnableVoteWarning;
  199. FollowButton.Visible = false;
  200. var voteList = AvailableVoteOptions[(StandardVoteType)obj.Id].Dropdowns;
  201. VoteOptionsButtonContainer.RemoveAllChildren();
  202. if (voteList != null)
  203. {
  204. int i = 0;
  205. foreach (var voteDropdown in voteList)
  206. {
  207. var optionButton = new OptionButton();
  208. int j = 0;
  209. foreach (var (key, value) in voteDropdown)
  210. {
  211. optionButton.AddItem(Loc.GetString(value), j);
  212. j++;
  213. }
  214. VoteOptionsButtonContainer.AddChild(optionButton);
  215. optionButton.Visible = true;
  216. optionButton.OnItemSelected += ButtonSelected;
  217. optionButton.Margin = new Thickness(2, 1);
  218. if (AvailableVoteOptions[(StandardVoteType)obj.Id].FollowDropdownId != null && AvailableVoteOptions[(StandardVoteType)obj.Id].FollowDropdownId == i)
  219. {
  220. _followDropdown = optionButton;
  221. FollowButton.Visible = true;
  222. }
  223. i++;
  224. }
  225. }
  226. }
  227. protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
  228. {
  229. return DragMode.Move;
  230. }
  231. }
  232. [UsedImplicitly, AnyCommand]
  233. public sealed class VoteMenuCommand : IConsoleCommand
  234. {
  235. public string Command => "votemenu";
  236. public string Description => Loc.GetString("ui-vote-menu-command-description");
  237. public string Help => Loc.GetString("ui-vote-menu-command-help-text");
  238. public void Execute(IConsoleShell shell, string argStr, string[] args)
  239. {
  240. new VoteCallMenu().OpenCentered();
  241. }
  242. }
  243. public record struct CreateVoteOption
  244. {
  245. public string Name;
  246. public List<Dictionary<string, string>> Dropdowns;
  247. public bool EnableVoteWarning;
  248. public int? FollowDropdownId; // If set, this will enable the Follow button and use the dropdown matching the ID as input.
  249. public CreateVoteOption(string name, List<Dictionary<string, string>> dropdowns, bool enableVoteWarning, int? followDropdownId)
  250. {
  251. Name = name;
  252. Dropdowns = dropdowns;
  253. EnableVoteWarning = enableVoteWarning;
  254. FollowDropdownId = followDropdownId;
  255. }
  256. }
  257. }