| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- using System.Diagnostics.CodeAnalysis;
- using System.Linq;
- using System.Numerics;
- using Content.Client.Administration.Managers;
- using Content.Client.Administration.Systems;
- using Content.Client.Administration.UI.Bwoink;
- using Content.Client.Gameplay;
- using Content.Client.Lobby;
- using Content.Client.Lobby.UI;
- using Content.Client.Stylesheets;
- using Content.Client.UserInterface.Controls;
- using Content.Client.UserInterface.Systems.MenuBar.Widgets;
- using Content.Shared.Administration;
- using Content.Shared.CCVar;
- using Content.Shared.Input;
- using JetBrains.Annotations;
- using Robust.Client.Audio;
- using Robust.Client.Graphics;
- using Robust.Client.Player;
- using Robust.Client.UserInterface;
- using Robust.Client.UserInterface.Controllers;
- using Robust.Client.UserInterface.Controls;
- using Robust.Client.UserInterface.CustomControls;
- using Robust.Shared.Configuration;
- using Robust.Shared.Input.Binding;
- using Robust.Shared.Network;
- using Robust.Shared.Player;
- using Robust.Shared.Utility;
- namespace Content.Client.UserInterface.Systems.Bwoink;
- [UsedImplicitly]
- public sealed class AHelpUIController: UIController, IOnSystemChanged<BwoinkSystem>, IOnStateChanged<GameplayState>, IOnStateChanged<LobbyState>
- {
- [Dependency] private readonly IClientAdminManager _adminManager = default!;
- [Dependency] private readonly IConfigurationManager _config = default!;
- [Dependency] private readonly IPlayerManager _playerManager = default!;
- [Dependency] private readonly IClyde _clyde = default!;
- [Dependency] private readonly IUserInterfaceManager _uiManager = default!;
- [UISystemDependency] private readonly AudioSystem _audio = default!;
- private BwoinkSystem? _bwoinkSystem;
- private MenuButton? GameAHelpButton => UIManager.GetActiveUIWidgetOrNull<GameTopMenuBar>()?.AHelpButton;
- private Button? LobbyAHelpButton => (UIManager.ActiveScreen as LobbyGui)?.AHelpButton;
- public IAHelpUIHandler? UIHelper;
- private bool _discordRelayActive;
- private bool _hasUnreadAHelp;
- private bool _bwoinkSoundEnabled;
- private string? _aHelpSound;
- public override void Initialize()
- {
- base.Initialize();
- SubscribeNetworkEvent<BwoinkDiscordRelayUpdated>(DiscordRelayUpdated);
- SubscribeNetworkEvent<BwoinkPlayerTypingUpdated>(PeopleTypingUpdated);
- _adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
- _config.OnValueChanged(CCVars.AHelpSound, v => _aHelpSound = v, true);
- _config.OnValueChanged(CCVars.BwoinkSoundEnabled, v => _bwoinkSoundEnabled = v, true);
- }
- public void UnloadButton()
- {
- if (GameAHelpButton != null)
- GameAHelpButton.OnPressed -= AHelpButtonPressed;
- if (LobbyAHelpButton != null)
- LobbyAHelpButton.OnPressed -= AHelpButtonPressed;
- }
- public void LoadButton()
- {
- if (GameAHelpButton != null)
- GameAHelpButton.OnPressed += AHelpButtonPressed;
- if (LobbyAHelpButton != null)
- LobbyAHelpButton.OnPressed += AHelpButtonPressed;
- }
- private void OnAdminStatusUpdated()
- {
- if (UIHelper is not { IsOpen: true })
- return;
- EnsureUIHelper();
- }
- private void AHelpButtonPressed(BaseButton.ButtonEventArgs obj)
- {
- EnsureUIHelper();
- UIHelper!.ToggleWindow();
- }
- public void OnSystemLoaded(BwoinkSystem system)
- {
- _bwoinkSystem = system;
- _bwoinkSystem.OnBwoinkTextMessageRecieved += ReceivedBwoink;
- CommandBinds.Builder
- .Bind(ContentKeyFunctions.OpenAHelp,
- InputCmdHandler.FromDelegate(_ => ToggleWindow()))
- .Register<AHelpUIController>();
- }
- public void OnSystemUnloaded(BwoinkSystem system)
- {
- CommandBinds.Unregister<AHelpUIController>();
- DebugTools.Assert(_bwoinkSystem != null);
- _bwoinkSystem!.OnBwoinkTextMessageRecieved -= ReceivedBwoink;
- _bwoinkSystem = null;
- }
- private void SetAHelpPressed(bool pressed)
- {
- if (GameAHelpButton != null)
- {
- GameAHelpButton.Pressed = pressed;
- }
- if (LobbyAHelpButton != null)
- {
- LobbyAHelpButton.Pressed = pressed;
- }
- UIManager.ClickSound();
- UnreadAHelpRead();
- }
- private void ReceivedBwoink(object? sender, SharedBwoinkSystem.BwoinkTextMessage message)
- {
- Logger.InfoS("c.s.go.es.bwoink", $"@{message.UserId}: {message.Text}");
- var localPlayer = _playerManager.LocalSession;
- if (localPlayer == null)
- {
- return;
- }
- if (message.PlaySound && localPlayer.UserId != message.TrueSender)
- {
- if (_aHelpSound != null && (_bwoinkSoundEnabled || !_adminManager.IsActive()))
- _audio.PlayGlobal(_aHelpSound, Filter.Local(), false);
- _clyde.RequestWindowAttention();
- }
- EnsureUIHelper();
- if (!UIHelper!.IsOpen)
- {
- UnreadAHelpReceived();
- }
- UIHelper!.Receive(message);
- }
- private void DiscordRelayUpdated(BwoinkDiscordRelayUpdated args, EntitySessionEventArgs session)
- {
- _discordRelayActive = args.DiscordRelayEnabled;
- UIHelper?.DiscordRelayChanged(_discordRelayActive);
- }
- private void PeopleTypingUpdated(BwoinkPlayerTypingUpdated args, EntitySessionEventArgs session)
- {
- UIHelper?.PeopleTypingUpdated(args);
- }
- public void EnsureUIHelper()
- {
- var isAdmin = _adminManager.HasFlag(AdminFlags.Adminhelp);
- if (UIHelper != null && UIHelper.IsAdmin == isAdmin)
- return;
- UIHelper?.Dispose();
- var ownerUserId = _playerManager.LocalUser!.Value;
- UIHelper = isAdmin ? new AdminAHelpUIHandler(ownerUserId) : new UserAHelpUIHandler(ownerUserId);
- UIHelper.DiscordRelayChanged(_discordRelayActive);
- UIHelper.SendMessageAction = (userId, textMessage, playSound, adminOnly) => _bwoinkSystem?.Send(userId, textMessage, playSound, adminOnly);
- UIHelper.InputTextChanged += (channel, text) => _bwoinkSystem?.SendInputTextUpdated(channel, text.Length > 0);
- UIHelper.OnClose += () => { SetAHelpPressed(false); };
- UIHelper.OnOpen += () => { SetAHelpPressed(true); };
- SetAHelpPressed(UIHelper.IsOpen);
- }
- public void Open()
- {
- var localUser = _playerManager.LocalUser;
- if (localUser == null)
- {
- return;
- }
- EnsureUIHelper();
- if (UIHelper!.IsOpen)
- return;
- UIHelper!.Open(localUser.Value, _discordRelayActive);
- }
- public void Open(NetUserId userId)
- {
- EnsureUIHelper();
- if (!UIHelper!.IsAdmin)
- return;
- UIHelper?.Open(userId, _discordRelayActive);
- }
- public void ToggleWindow()
- {
- EnsureUIHelper();
- UIHelper?.ToggleWindow();
- }
- public void PopOut()
- {
- EnsureUIHelper();
- if (UIHelper is not AdminAHelpUIHandler helper)
- return;
- if (helper.Window == null || helper.Control == null)
- {
- return;
- }
- helper.Control.Orphan();
- helper.Window.Dispose();
- helper.Window = null;
- helper.EverOpened = false;
- var monitor = _clyde.EnumerateMonitors().First();
- helper.ClydeWindow = _clyde.CreateWindow(new WindowCreateParameters
- {
- Maximized = false,
- Title = "Admin Help",
- Monitor = monitor,
- Width = 900,
- Height = 500
- });
- helper.ClydeWindow.RequestClosed += helper.OnRequestClosed;
- helper.ClydeWindow.DisposeOnClose = true;
- helper.WindowRoot = _uiManager.CreateWindowRoot(helper.ClydeWindow);
- helper.WindowRoot.AddChild(helper.Control);
- helper.Control.PopOut.Disabled = true;
- helper.Control.PopOut.Visible = false;
- }
- private void UnreadAHelpReceived()
- {
- GameAHelpButton?.StyleClasses.Add(MenuButton.StyleClassRedTopButton);
- LobbyAHelpButton?.StyleClasses.Add(StyleNano.StyleClassButtonColorRed);
- _hasUnreadAHelp = true;
- }
- private void UnreadAHelpRead()
- {
- GameAHelpButton?.StyleClasses.Remove(MenuButton.StyleClassRedTopButton);
- LobbyAHelpButton?.StyleClasses.Remove(StyleNano.StyleClassButtonColorRed);
- _hasUnreadAHelp = false;
- }
- public void OnStateEntered(GameplayState state)
- {
- if (GameAHelpButton != null)
- {
- GameAHelpButton.OnPressed -= AHelpButtonPressed;
- GameAHelpButton.OnPressed += AHelpButtonPressed;
- GameAHelpButton.Pressed = UIHelper?.IsOpen ?? false;
- if (_hasUnreadAHelp)
- {
- UnreadAHelpReceived();
- }
- else
- {
- UnreadAHelpRead();
- }
- }
- }
- public void OnStateExited(GameplayState state)
- {
- if (GameAHelpButton != null)
- GameAHelpButton.OnPressed -= AHelpButtonPressed;
- }
- public void OnStateEntered(LobbyState state)
- {
- if (LobbyAHelpButton != null)
- {
- LobbyAHelpButton.OnPressed -= AHelpButtonPressed;
- LobbyAHelpButton.OnPressed += AHelpButtonPressed;
- LobbyAHelpButton.Pressed = UIHelper?.IsOpen ?? false;
- if (_hasUnreadAHelp)
- {
- UnreadAHelpReceived();
- }
- else
- {
- UnreadAHelpRead();
- }
- }
- }
- public void OnStateExited(LobbyState state)
- {
- if (LobbyAHelpButton != null)
- LobbyAHelpButton.OnPressed -= AHelpButtonPressed;
- }
- }
- // please kill all this indirection
- public interface IAHelpUIHandler : IDisposable
- {
- public bool IsAdmin { get; }
- public bool IsOpen { get; }
- public void Receive(SharedBwoinkSystem.BwoinkTextMessage message);
- public void Close();
- public void Open(NetUserId netUserId, bool relayActive);
- public void ToggleWindow();
- public void DiscordRelayChanged(bool active);
- public void PeopleTypingUpdated(BwoinkPlayerTypingUpdated args);
- public event Action OnClose;
- public event Action OnOpen;
- public Action<NetUserId, string, bool, bool>? SendMessageAction { get; set; }
- public event Action<NetUserId, string>? InputTextChanged;
- }
- public sealed class AdminAHelpUIHandler : IAHelpUIHandler
- {
- private readonly NetUserId _ownerId;
- public AdminAHelpUIHandler(NetUserId owner)
- {
- _ownerId = owner;
- }
- private readonly Dictionary<NetUserId, BwoinkPanel> _activePanelMap = new();
- public bool IsAdmin => true;
- public bool IsOpen => Window is { Disposed: false, IsOpen: true } || ClydeWindow is { IsDisposed: false };
- public bool EverOpened;
- public BwoinkWindow? Window;
- public WindowRoot? WindowRoot;
- public IClydeWindow? ClydeWindow;
- public BwoinkControl? Control;
- public void Receive(SharedBwoinkSystem.BwoinkTextMessage message)
- {
- var panel = EnsurePanel(message.UserId);
- panel.ReceiveLine(message);
- Control?.OnBwoink(message.UserId);
- }
- private void OpenWindow()
- {
- if (Window == null)
- return;
- if (EverOpened)
- Window.Open();
- else
- Window.OpenCentered();
- }
- public void Close()
- {
- Window?.Close();
- // popped-out window is being closed
- if (ClydeWindow != null)
- {
- ClydeWindow.RequestClosed -= OnRequestClosed;
- ClydeWindow.Dispose();
- // need to dispose control cause we cant reattach it directly back to the window
- // but orphan panels first so -they- can get readded when the window is opened again
- if (Control != null)
- {
- foreach (var (_, panel) in _activePanelMap)
- {
- panel.Orphan();
- }
- Control?.Dispose();
- }
- // window wont be closed here so we will invoke ourselves
- OnClose?.Invoke();
- }
- }
- public void ToggleWindow()
- {
- EnsurePanel(_ownerId);
- if (IsOpen)
- Close();
- else
- OpenWindow();
- }
- public void DiscordRelayChanged(bool active)
- {
- }
- public void PeopleTypingUpdated(BwoinkPlayerTypingUpdated args)
- {
- if (_activePanelMap.TryGetValue(args.Channel, out var panel))
- panel.UpdatePlayerTyping(args.PlayerName, args.Typing);
- }
- public event Action? OnClose;
- public event Action? OnOpen;
- public Action<NetUserId, string, bool, bool>? SendMessageAction { get; set; }
- public event Action<NetUserId, string>? InputTextChanged;
- public void Open(NetUserId channelId, bool relayActive)
- {
- SelectChannel(channelId);
- OpenWindow();
- }
- public void OnRequestClosed(WindowRequestClosedEventArgs args)
- {
- Close();
- }
- private void EnsureControl()
- {
- if (Control is { Disposed: false })
- return;
- Window = new BwoinkWindow();
- Control = Window.Bwoink;
- Window.OnClose += () => { OnClose?.Invoke(); };
- Window.OnOpen += () =>
- {
- OnOpen?.Invoke();
- EverOpened = true;
- };
- // need to readd any unattached panels..
- foreach (var (_, panel) in _activePanelMap)
- {
- if (!Control!.BwoinkArea.Children.Contains(panel))
- {
- Control!.BwoinkArea.AddChild(panel);
- }
- panel.Visible = false;
- }
- }
- public void HideAllPanels()
- {
- foreach (var panel in _activePanelMap.Values)
- {
- panel.Visible = false;
- }
- }
- public BwoinkPanel EnsurePanel(NetUserId channelId)
- {
- EnsureControl();
- if (_activePanelMap.TryGetValue(channelId, out var existingPanel))
- return existingPanel;
- _activePanelMap[channelId] = existingPanel = new BwoinkPanel(text => SendMessageAction?.Invoke(channelId, text, Window?.Bwoink.PlaySound.Pressed ?? true, Window?.Bwoink.AdminOnly.Pressed ?? false));
- existingPanel.InputTextChanged += text => InputTextChanged?.Invoke(channelId, text);
- existingPanel.Visible = false;
- if (!Control!.BwoinkArea.Children.Contains(existingPanel))
- Control.BwoinkArea.AddChild(existingPanel);
- return existingPanel;
- }
- public bool TryGetChannel(NetUserId ch, [NotNullWhen(true)] out BwoinkPanel? bp) => _activePanelMap.TryGetValue(ch, out bp);
- private void SelectChannel(NetUserId uid)
- {
- EnsurePanel(uid);
- Control!.SelectChannel(uid);
- }
- public void Dispose()
- {
- Window?.Dispose();
- Window = null;
- Control = null;
- _activePanelMap.Clear();
- EverOpened = false;
- }
- }
- public sealed class UserAHelpUIHandler : IAHelpUIHandler
- {
- private readonly NetUserId _ownerId;
- public UserAHelpUIHandler(NetUserId owner)
- {
- _ownerId = owner;
- }
- public bool IsAdmin => false;
- public bool IsOpen => _window is { Disposed: false, IsOpen: true };
- private DefaultWindow? _window;
- private BwoinkPanel? _chatPanel;
- private bool _discordRelayActive;
- public void Receive(SharedBwoinkSystem.BwoinkTextMessage message)
- {
- DebugTools.Assert(message.UserId == _ownerId);
- EnsureInit(_discordRelayActive);
- _chatPanel!.ReceiveLine(message);
- _window!.OpenCentered();
- }
- public void Close()
- {
- _window?.Close();
- }
- public void ToggleWindow()
- {
- EnsureInit(_discordRelayActive);
- if (_window!.IsOpen)
- {
- _window.Close();
- }
- else
- {
- _window.OpenCentered();
- }
- }
- // user can't pop out their window.
- public void PopOut()
- {
- }
- public void DiscordRelayChanged(bool active)
- {
- _discordRelayActive = active;
- if (_chatPanel != null)
- {
- _chatPanel.RelayedToDiscordLabel.Visible = active;
- }
- }
- public void PeopleTypingUpdated(BwoinkPlayerTypingUpdated args)
- {
- }
- public event Action? OnClose;
- public event Action? OnOpen;
- public Action<NetUserId, string, bool, bool>? SendMessageAction { get; set; }
- public event Action<NetUserId, string>? InputTextChanged;
- public void Open(NetUserId channelId, bool relayActive)
- {
- EnsureInit(relayActive);
- _window!.OpenCentered();
- }
- private void EnsureInit(bool relayActive)
- {
- if (_window is { Disposed: false })
- return;
- _chatPanel = new BwoinkPanel(text => SendMessageAction?.Invoke(_ownerId, text, true, false));
- _chatPanel.InputTextChanged += text => InputTextChanged?.Invoke(_ownerId, text);
- _chatPanel.RelayedToDiscordLabel.Visible = relayActive;
- _window = new DefaultWindow()
- {
- TitleClass="windowTitleAlert",
- HeaderClass="windowHeaderAlert",
- Title=Loc.GetString("bwoink-user-title"),
- MinSize = new Vector2(500, 300),
- };
- _window.OnClose += () => { OnClose?.Invoke(); };
- _window.OnOpen += () => { OnOpen?.Invoke(); };
- _window.Contents.AddChild(_chatPanel);
- var introText = Loc.GetString("bwoink-system-introductory-message");
- var introMessage = new SharedBwoinkSystem.BwoinkTextMessage( _ownerId, SharedBwoinkSystem.SystemUserId, introText);
- Receive(introMessage);
- }
- public void Dispose()
- {
- _window?.Dispose();
- _window = null;
- _chatPanel = null;
- }
- }
|