From 0e6b55de66ecaa1f460f28a4ad1132cceb7e8be0 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Wed, 26 Feb 2020 16:42:12 +0100 Subject: [PATCH] Clean up client game ticker status code. Fixes #495 --- Content.Client/EntryPoint.cs | 6 - Content.Client/EscapeMenuOwner.cs | 4 +- .../GameObjects/EntitySystems/VerbSystem.cs | 2 +- .../GameTicking/ClientGameTicker.cs | 306 ++---------------- .../Interfaces/IClientGameTicker.cs | 10 +- Content.Client/State/GameScreen.cs | 180 +++-------- Content.Client/State/GameScreenBase.cs | 160 +++++++++ Content.Client/State/LobbyState.cs | 188 +++++++++++ Content.Client/State/StateBase.cs | 32 ++ .../UserInterface/CharacterSetupGui.cs | 12 +- .../UserInterface/HumanoidProfileEditor.cs | 23 +- .../LobbyCharacterPreviewPanel.cs | 5 +- Content.Client/UserInterface/LobbyGui.cs | 18 +- Content.Client/UserInterface/ServerInfo.cs | 6 +- 14 files changed, 484 insertions(+), 468 deletions(-) create mode 100644 Content.Client/State/GameScreenBase.cs create mode 100644 Content.Client/State/LobbyState.cs create mode 100644 Content.Client/State/StateBase.cs diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index e9765b3718..197a1e8ebe 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -178,11 +178,6 @@ namespace Content.Client _escapeMenuOwner.Initialize(); - _baseClient.PlayerJoinedGame += (sender, args) => - { - _stateManager.RequestStateChange(); - }; - _baseClient.PlayerJoinedServer += (sender, args) => { IoCManager.Resolve().CreateNewMapEntity(MapId.Nullspace); @@ -242,7 +237,6 @@ namespace Content.Client { case ModUpdateLevel.FramePreEngine: IoCManager.Resolve().FrameUpdate(frameEventArgs); - IoCManager.Resolve().FrameUpdate(frameEventArgs); IoCManager.Resolve().FrameUpdate(frameEventArgs); break; } diff --git a/Content.Client/EscapeMenuOwner.cs b/Content.Client/EscapeMenuOwner.cs index 53cee94814..519d6e0692 100644 --- a/Content.Client/EscapeMenuOwner.cs +++ b/Content.Client/EscapeMenuOwner.cs @@ -41,7 +41,7 @@ namespace Content.Client private void StateManagerOnOnStateChanged(StateChangedEventArgs obj) { - if (obj.NewState is GameScreen) + if (obj.NewState is GameScreenBase) { // Switched TO GameScreen. _escapeMenu = new EscapeMenu(_clientConsole, _tileDefinitionManager, _placementManager, @@ -52,7 +52,7 @@ namespace Content.Client _inputManager.SetInputCommand(EngineKeyFunctions.EscapeMenu, InputCmdHandler.FromDelegate(s => Enabled())); } - else if (obj.OldState is GameScreen) + else if (obj.OldState is GameScreenBase) { // Switched FROM GameScreen. _escapeMenu.Dispose(); diff --git a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs index c6f5ae5d36..832ea53501 100644 --- a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs @@ -77,7 +77,7 @@ namespace Content.Client.GameObjects.EntitySystems return true; } - if (!(_stateManager.CurrentState is GameScreen gameScreen)) + if (!(_stateManager.CurrentState is GameScreenBase gameScreen)) { return false; } diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs index 18210f44eb..77ca609371 100644 --- a/Content.Client/GameTicking/ClientGameTicker.cs +++ b/Content.Client/GameTicking/ClientGameTicker.cs @@ -1,26 +1,10 @@ using System; -using System.Linq; -using Content.Client.Chat; using Content.Client.Interfaces; -using Content.Client.Interfaces.Chat; -using Content.Client.UserInterface; +using Content.Client.State; using Content.Shared; -using Content.Shared.Input; -using Robust.Client; -using Robust.Client.Console; -using Robust.Client.Interfaces; -using Robust.Client.Interfaces.Input; -using Robust.Client.Interfaces.ResourceManagement; -using Robust.Client.Interfaces.UserInterface; -using Robust.Client.Player; -using Robust.Client.UserInterface.Controls; -using Robust.Shared.Input; -using Robust.Shared.Interfaces.GameObjects; +using Robust.Client.Interfaces.State; using Robust.Shared.Interfaces.Network; using Robust.Shared.IoC; -using Robust.Shared.Localization; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; @@ -30,289 +14,55 @@ namespace Content.Client.GameTicking { #pragma warning disable 649 [Dependency] private IClientNetManager _netManager; - [Dependency] private IUserInterfaceManager _userInterfaceManager; - [Dependency] private IInputManager _inputManager; - [Dependency] private IBaseClient _baseClient; - [Dependency] private IChatManager _chatManager; - [Dependency] private IClientConsole _console; - [Dependency] private ILocalizationManager _localization; - [Dependency] private IResourceCache _resourceCache; - [Dependency] private IPlayerManager _playerManager; - [Dependency] private IGameHud _gameHud; - [Dependency] private IEntityManager _entityManager; - [Dependency] private IClientPreferencesManager _preferencesManager; - [Dependency] private IPrototypeManager _prototypeManager; + [Dependency] private IStateManager _stateManager; #pragma warning restore 649 - [ViewVariables] private bool _areWeReady; - [ViewVariables] private CharacterSetupGui _characterSetup; - [ViewVariables] private ChatBox _gameChat; - [ViewVariables] private bool _gameStarted; [ViewVariables] private bool _initialized; - [ViewVariables] private LobbyGui _lobby; - [ViewVariables] private string _serverInfoBlob; - [ViewVariables] private DateTime _startTime; - [ViewVariables] private TickerState _tickerState; + + [ViewVariables] public bool AreWeReady { get; private set; } + [ViewVariables] public bool IsGameStarted { get; private set; } + [ViewVariables] public string ServerInfoBlob { get; private set; } + [ViewVariables] public DateTime StartTime { get; private set; } + + public event Action InfoBlobUpdated; + public event Action LobbyStatusUpdated; public void Initialize() { DebugTools.Assert(!_initialized); - _netManager.RegisterNetMessage(nameof(MsgTickerJoinLobby), _joinLobby); - _netManager.RegisterNetMessage(nameof(MsgTickerJoinGame), _joinGame); - _netManager.RegisterNetMessage(nameof(MsgTickerLobbyStatus), _lobbyStatus); - _netManager.RegisterNetMessage(nameof(MsgTickerLobbyInfo), _lobbyInfo); - - _baseClient.RunLevelChanged += BaseClientOnRunLevelChanged; - _playerManager.PlayerListUpdated += PlayerManagerOnPlayerListUpdated; + _netManager.RegisterNetMessage(nameof(MsgTickerJoinLobby), JoinLobby); + _netManager.RegisterNetMessage(nameof(MsgTickerJoinGame), JoinGame); + _netManager.RegisterNetMessage(nameof(MsgTickerLobbyStatus), LobbyStatus); + _netManager.RegisterNetMessage(nameof(MsgTickerLobbyInfo), LobbyInfo); _initialized = true; } - private void PlayerManagerOnPlayerListUpdated(object sender, EventArgs e) + private void JoinLobby(MsgTickerJoinLobby message) { - if (_lobby == null) - { - return; - } - - _updatePlayerList(); + _stateManager.RequestStateChange(); } - private void _updatePlayerList() + private void LobbyStatus(MsgTickerLobbyStatus message) { - _lobby.OnlinePlayerItemList.Clear(); - foreach (var session in _playerManager.Sessions.OrderBy(s => s.Name)) - { - _lobby.OnlinePlayerItemList.AddItem(session.Name); - } + StartTime = message.StartTime; + IsGameStarted = message.IsRoundStarted; + AreWeReady = message.YouAreReady; + + LobbyStatusUpdated?.Invoke(); } - private void BaseClientOnRunLevelChanged(object sender, RunLevelChangedEventArgs e) + private void LobbyInfo(MsgTickerLobbyInfo message) { - if (e.NewLevel != ClientRunLevel.Initialize) - { - _inputManager.SetInputCommand(ContentKeyFunctions.FocusChat, null); - return; - } + ServerInfoBlob = message.TextBlob; - _tickerState = TickerState.Unset; - _lobby?.Dispose(); - _lobby = null; - _gameChat?.Dispose(); - _gameChat = null; - _gameHud.RootControl.Orphan(); + InfoBlobUpdated?.Invoke(); } - public void FrameUpdate(FrameEventArgs frameEventArgs) + private void JoinGame(MsgTickerJoinGame message) { - if (_lobby == null) - { - return; - } - - if (_gameStarted) - { - _lobby.StartTime.Text = ""; - return; - } - - string text; - var difference = _startTime - DateTime.UtcNow; - if (difference.Ticks < 0) - { - if (difference.TotalSeconds < -5) - { - text = _localization.GetString("Right Now?"); - } - else - { - text = _localization.GetString("Right Now"); - } - } - else - { - text = $"{(int) Math.Floor(difference.TotalMinutes)}:{difference.Seconds:D2}"; - } - - _lobby.StartTime.Text = _localization.GetString("Round Starts In: {0}", text); - } - - private void _lobbyStatus(MsgTickerLobbyStatus message) - { - _startTime = message.StartTime; - _gameStarted = message.IsRoundStarted; - _areWeReady = message.YouAreReady; - - _updateLobbyUi(); - } - - private void _lobbyInfo(MsgTickerLobbyInfo message) - { - _serverInfoBlob = message.TextBlob; - - _updateLobbyUi(); - } - - private void _updateLobbyUi() - { - if (_lobby == null) - { - return; - } - - if (_gameStarted) - { - _lobby.ReadyButton.Text = _localization.GetString("Join"); - _lobby.ReadyButton.ToggleMode = false; - _lobby.ReadyButton.Pressed = false; - } - else - { - _lobby.StartTime.Text = ""; - _lobby.ReadyButton.Text = _localization.GetString("Ready Up"); - _lobby.ReadyButton.ToggleMode = true; - _lobby.ReadyButton.Pressed = _areWeReady; - } - - _lobby.ServerInfo.SetInfoBlob(_serverInfoBlob); - } - - private void _joinLobby(MsgTickerJoinLobby message) - { - if (_tickerState == TickerState.InLobby) - { - return; - } - - if (_gameChat != null) - { - _gameChat.Dispose(); - _gameChat = null; - } - - _gameHud.RootControl.Orphan(); - - _tickerState = TickerState.InLobby; - - _characterSetup = new CharacterSetupGui(_entityManager, _localization, _resourceCache, _preferencesManager, _prototypeManager); - LayoutContainer.SetAnchorPreset(_characterSetup, LayoutContainer.LayoutPreset.Wide); - _characterSetup.CloseButton.OnPressed += args => - { - _characterSetup.Save(); - _lobby.CharacterPreview.UpdateUI(); - _userInterfaceManager.StateRoot.AddChild(_lobby); - _userInterfaceManager.StateRoot.RemoveChild(_characterSetup); - }; - _lobby = new LobbyGui(_entityManager, _localization, _resourceCache, _preferencesManager); - _userInterfaceManager.StateRoot.AddChild(_lobby); - - LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide); - - _chatManager.SetChatBox(_lobby.Chat); - _lobby.Chat.DefaultChatFormat = "ooc \"{0}\""; - - _lobby.ServerName.Text = _baseClient.GameInfo.ServerName; - - _inputManager.SetInputCommand(ContentKeyFunctions.FocusChat, - InputCmdHandler.FromDelegate(s => _focusChat(_lobby.Chat))); - - _updateLobbyUi(); - - _lobby.CharacterPreview.CharacterSetupButton.OnPressed += args => - { - SetReady(false); - _userInterfaceManager.StateRoot.RemoveChild(_lobby); - _userInterfaceManager.StateRoot.AddChild(_characterSetup); - }; - - _lobby.ObserveButton.OnPressed += args => _console.ProcessCommand("observe"); - _lobby.ReadyButton.OnPressed += args => - { - if (!_gameStarted) - { - return; - } - - _console.ProcessCommand("joingame"); - }; - - _lobby.ReadyButton.OnToggled += args => - { - SetReady(args.Pressed); - }; - - _lobby.LeaveButton.OnPressed += args => _console.ProcessCommand("disconnect"); - - _updatePlayerList(); - } - - private void SetReady(bool newReady) - { - if (_gameStarted) - { - return; - } - - _console.ProcessCommand($"toggleready {newReady}"); - } - - private void _joinGame(MsgTickerJoinGame message) - { - if (_tickerState == TickerState.InGame) - { - return; - } - - _tickerState = TickerState.InGame; - - if (_lobby != null) - { - _lobby.Dispose(); - _lobby = null; - } - - _gameChat = new ChatBox(); - _userInterfaceManager.StateRoot.AddChild(_gameChat); - LayoutContainer.SetAnchorAndMarginPreset(_gameChat, LayoutContainer.LayoutPreset.TopRight, margin: 10); - LayoutContainer.SetAnchorAndMarginPreset(_gameChat, LayoutContainer.LayoutPreset.TopRight, margin: 10); - LayoutContainer.SetMarginLeft(_gameChat, -475); - LayoutContainer.SetMarginBottom(_gameChat, 235); - - _userInterfaceManager.StateRoot.AddChild(_gameHud.RootControl); - _chatManager.SetChatBox(_gameChat); - _gameChat.DefaultChatFormat = "say \"{0}\""; - _gameChat.Input.PlaceHolder = _localization.GetString("Say something! [ for OOC"); - - _inputManager.SetInputCommand(ContentKeyFunctions.FocusChat, - InputCmdHandler.FromDelegate(s => _focusChat(_gameChat))); - } - - private void _focusChat(ChatBox chat) - { - if (chat == null || _userInterfaceManager.KeyboardFocused != null) - { - return; - } - - chat.Input.IgnoreNext = true; - chat.Input.GrabKeyboardFocus(); - } - - private enum TickerState - { - Unset = 0, - - /// - /// The client is in the lobby. - /// - InLobby = 1, - - /// - /// The client is NOT in the lobby. - /// Do not confuse this with the client session status. - /// - InGame = 2 + _stateManager.RequestStateChange(); } } } diff --git a/Content.Client/Interfaces/IClientGameTicker.cs b/Content.Client/Interfaces/IClientGameTicker.cs index 02ccd8a367..5e92e3d27c 100644 --- a/Content.Client/Interfaces/IClientGameTicker.cs +++ b/Content.Client/Interfaces/IClientGameTicker.cs @@ -1,10 +1,16 @@ -using Robust.Shared.Timing; +using System; namespace Content.Client.Interfaces { public interface IClientGameTicker { + bool IsGameStarted { get; } + string ServerInfoBlob { get; } + bool AreWeReady { get; } + DateTime StartTime { get; } + void Initialize(); - void FrameUpdate(FrameEventArgs FrameEventArgs); + event Action InfoBlobUpdated; + event Action LobbyStatusUpdated; } } diff --git a/Content.Client/State/GameScreen.cs b/Content.Client/State/GameScreen.cs index 8100609da0..3deb982aae 100644 --- a/Content.Client/State/GameScreen.cs +++ b/Content.Client/State/GameScreen.cs @@ -1,171 +1,65 @@ -using System.Collections.Generic; -using System.Linq; -using Content.Client.GameObjects.Components; -using Content.Shared.GameObjects; -using Robust.Client.GameObjects.EntitySystems; -using Robust.Client.Interfaces.GameObjects; -using Robust.Client.Interfaces.GameObjects.Components; -using Robust.Client.Interfaces.Graphics.ClientEye; +using Content.Client.Chat; +using Content.Client.Interfaces.Chat; +using Content.Client.UserInterface; +using Content.Shared.Input; using Robust.Client.Interfaces.Input; -using Robust.Client.Interfaces.Placement; -using Robust.Client.Player; -using Robust.Shared.GameObjects; +using Robust.Client.Interfaces.UserInterface; +using Robust.Client.UserInterface.Controls; using Robust.Shared.Input; -using Robust.Shared.Interfaces.GameObjects; -using Robust.Shared.Interfaces.Map; -using Robust.Shared.Interfaces.Timing; using Robust.Shared.IoC; -using Robust.Shared.Map; -using Robust.Shared.Timing; +using Robust.Shared.Localization; +using Robust.Shared.ViewVariables; namespace Content.Client.State { - // OH GOD. - // Ok actually it's fine. - // Instantiated dynamically through the StateManager, Dependencies will be resolved. - public sealed partial class GameScreen : Robust.Client.State.State + public class GameScreen : GameScreenBase { #pragma warning disable 649 - [Dependency] private readonly IClientEntityManager _entityManager; - [Dependency] private readonly IComponentManager _componentManager; - [Dependency] private readonly IInputManager inputManager; - [Dependency] private readonly IPlayerManager playerManager; - [Dependency] private readonly IPlacementManager placementManager; - [Dependency] private readonly IEyeManager eyeManager; - [Dependency] private readonly IEntitySystemManager entitySystemManager; - [Dependency] private readonly IGameTiming timing; - [Dependency] private readonly IMapManager _mapManager; + [Dependency] private readonly IUserInterfaceManager _userInterfaceManager; + [Dependency] private readonly IGameHud _gameHud; + [Dependency] private readonly IInputManager _inputManager; + [Dependency] private readonly IChatManager _chatManager; #pragma warning restore 649 - private IEntity lastHoveredEntity; + [ViewVariables] private ChatBox _gameChat; public override void Startup() { - inputManager.KeyBindStateChanged += OnKeyBindStateChanged; + base.Startup(); + + _gameChat = new ChatBox(); + _userInterfaceManager.StateRoot.AddChild(_gameChat); + LayoutContainer.SetAnchorAndMarginPreset(_gameChat, LayoutContainer.LayoutPreset.TopRight, margin: 10); + LayoutContainer.SetAnchorAndMarginPreset(_gameChat, LayoutContainer.LayoutPreset.TopRight, margin: 10); + LayoutContainer.SetMarginLeft(_gameChat, -475); + LayoutContainer.SetMarginBottom(_gameChat, 235); + + _userInterfaceManager.StateRoot.AddChild(_gameHud.RootControl); + _chatManager.SetChatBox(_gameChat); + _gameChat.DefaultChatFormat = "say \"{0}\""; + _gameChat.Input.PlaceHolder = Loc.GetString("Say something! [ for OOC"); + + _inputManager.SetInputCommand(ContentKeyFunctions.FocusChat, + InputCmdHandler.FromDelegate(s => FocusChat(_gameChat))); } public override void Shutdown() { - playerManager.LocalPlayer.DetachEntity(); + base.Shutdown(); - inputManager.KeyBindStateChanged -= OnKeyBindStateChanged; + _gameChat.Dispose(); + _gameHud.RootControl.Orphan(); } - public override void Update(FrameEventArgs e) + internal static void FocusChat(ChatBox chat) { - _componentManager.CullRemovedComponents(); - _entityManager.Update(e.DeltaSeconds); - playerManager.Update(e.DeltaSeconds); - } - - public override void FrameUpdate(FrameEventArgs e) - { - placementManager.FrameUpdate(e); - _entityManager.FrameUpdate(e.DeltaSeconds); - - var mousePosWorld = eyeManager.ScreenToWorld(new ScreenCoordinates(inputManager.MouseScreenPosition)); - var entityToClick = GetEntityUnderPosition(mousePosWorld); - - var inRange = false; - if (playerManager.LocalPlayer.ControlledEntity != null && entityToClick != null) + if (chat == null || chat.UserInterfaceManager.KeyboardFocused != null) { - var playerPos = playerManager.LocalPlayer.ControlledEntity.Transform.WorldPosition; - var entityPos = entityToClick.Transform.WorldPosition; - inRange = (entityPos - playerPos).Length <= VerbUtility.InteractionRange; - } - - InteractionOutlineComponent outline; - if (entityToClick == lastHoveredEntity) - { - if (entityToClick != null && entityToClick.TryGetComponent(out outline)) - { - outline.UpdateInRange(inRange); - } return; } - if (lastHoveredEntity != null && !lastHoveredEntity.Deleted && - lastHoveredEntity.TryGetComponent(out outline)) - { - outline.OnMouseLeave(); - } - - lastHoveredEntity = entityToClick; - - if (lastHoveredEntity != null && lastHoveredEntity.TryGetComponent(out outline)) - { - outline.OnMouseEnter(inRange); - } - } - - public IEntity GetEntityUnderPosition(GridCoordinates coordinates) - { - var entitiesUnderPosition = GetEntitiesUnderPosition(coordinates); - return entitiesUnderPosition.Count > 0 ? entitiesUnderPosition[0] : null; - } - - public IList GetEntitiesUnderPosition(GridCoordinates coordinates) - { - // Find all the entities intersecting our click - var mapCoords = coordinates.ToMap(_mapManager); - var entities = _entityManager.GetEntitiesIntersecting(mapCoords.MapId, mapCoords.Position); - - // Check the entities against whether or not we can click them - var foundEntities = new List<(IEntity clicked, int drawDepth)>(); - foreach (var entity in entities) - { - if (entity.TryGetComponent(out var component) - && entity.Transform.IsMapTransform - && component.CheckClick(coordinates.Position, out var drawDepthClicked)) - { - foundEntities.Add((entity, drawDepthClicked)); - } - } - - if (foundEntities.Count == 0) - return new List(); - - foundEntities.Sort(new ClickableEntityComparer()); - // 0 is the top element. - foundEntities.Reverse(); - return foundEntities.Select(a => a.clicked).ToList(); - } - - internal class ClickableEntityComparer : IComparer<(IEntity clicked, int depth)> - { - public int Compare((IEntity clicked, int depth) x, (IEntity clicked, int depth) y) - { - var val = x.depth.CompareTo(y.depth); - if (val != 0) - { - return val; - } - - var transx = x.clicked.Transform; - var transy = y.clicked.Transform; - return transx.GridPosition.Y.CompareTo(transy.GridPosition.Y); - } - } - - /// - /// Converts a state change event from outside the simulation to inside the simulation. - /// - /// Event data values for a bound key state change. - private void OnKeyBindStateChanged(BoundKeyEventArgs args) - { - var inputSys = entitySystemManager.GetEntitySystem(); - - var func = args.Function; - var funcId = inputManager.NetworkBindMap.KeyFunctionID(func); - - var mousePosWorld = eyeManager.ScreenToWorld(args.PointerLocation); - var entityToClick = GetEntityUnderPosition(mousePosWorld); - var message = new FullInputCmdMessage(timing.CurTick, funcId, args.State, mousePosWorld, - args.PointerLocation, entityToClick?.Uid ?? EntityUid.Invalid); - - // client side command handlers will always be sent the local player session. - var session = playerManager.LocalPlayer.Session; - inputSys.HandleInputCommand(session, func, message); + chat.Input.IgnoreNext = true; + chat.Input.GrabKeyboardFocus(); } } } diff --git a/Content.Client/State/GameScreenBase.cs b/Content.Client/State/GameScreenBase.cs new file mode 100644 index 0000000000..4cb8896e26 --- /dev/null +++ b/Content.Client/State/GameScreenBase.cs @@ -0,0 +1,160 @@ +using System.Collections.Generic; +using System.Linq; +using Content.Client.GameObjects.Components; +using Content.Shared.GameObjects; +using Robust.Client.GameObjects.EntitySystems; +using Robust.Client.Interfaces.GameObjects; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Client.Interfaces.Graphics.ClientEye; +using Robust.Client.Interfaces.Input; +using Robust.Client.Player; +using Robust.Shared.GameObjects; +using Robust.Shared.Input; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.Interfaces.Timing; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Timing; + +namespace Content.Client.State +{ + // OH GOD. + // Ok actually it's fine. + // Instantiated dynamically through the StateManager, Dependencies will be resolved. + public partial class GameScreenBase : StateBase + { +#pragma warning disable 649 + [Dependency] private readonly IClientEntityManager _entityManager; + [Dependency] private readonly IInputManager _inputManager; + [Dependency] private readonly IPlayerManager _playerManager; + [Dependency] private readonly IEyeManager _eyeManager; + [Dependency] private readonly IEntitySystemManager _entitySystemManager; + [Dependency] private readonly IGameTiming _timing; + [Dependency] private readonly IMapManager _mapManager; +#pragma warning restore 649 + + private IEntity _lastHoveredEntity; + + public override void Startup() + { + _inputManager.KeyBindStateChanged += OnKeyBindStateChanged; + } + + public override void Shutdown() + { + _playerManager.LocalPlayer.DetachEntity(); + + _inputManager.KeyBindStateChanged -= OnKeyBindStateChanged; + } + + public override void FrameUpdate(FrameEventArgs e) + { + base.FrameUpdate(e); + + var mousePosWorld = _eyeManager.ScreenToWorld(new ScreenCoordinates(_inputManager.MouseScreenPosition)); + var entityToClick = GetEntityUnderPosition(mousePosWorld); + + var inRange = false; + if (_playerManager.LocalPlayer.ControlledEntity != null && entityToClick != null) + { + var playerPos = _playerManager.LocalPlayer.ControlledEntity.Transform.WorldPosition; + var entityPos = entityToClick.Transform.WorldPosition; + inRange = (entityPos - playerPos).Length <= VerbUtility.InteractionRange; + } + + InteractionOutlineComponent outline; + if (entityToClick == _lastHoveredEntity) + { + if (entityToClick != null && entityToClick.TryGetComponent(out outline)) + { + outline.UpdateInRange(inRange); + } + return; + } + + if (_lastHoveredEntity != null && !_lastHoveredEntity.Deleted && + _lastHoveredEntity.TryGetComponent(out outline)) + { + outline.OnMouseLeave(); + } + + _lastHoveredEntity = entityToClick; + + if (_lastHoveredEntity != null && _lastHoveredEntity.TryGetComponent(out outline)) + { + outline.OnMouseEnter(inRange); + } + } + + public IEntity GetEntityUnderPosition(GridCoordinates coordinates) + { + var entitiesUnderPosition = GetEntitiesUnderPosition(coordinates); + return entitiesUnderPosition.Count > 0 ? entitiesUnderPosition[0] : null; + } + + public IList GetEntitiesUnderPosition(GridCoordinates coordinates) + { + // Find all the entities intersecting our click + var mapCoords = coordinates.ToMap(_mapManager); + var entities = _entityManager.GetEntitiesIntersecting(mapCoords.MapId, mapCoords.Position); + + // Check the entities against whether or not we can click them + var foundEntities = new List<(IEntity clicked, int drawDepth)>(); + foreach (var entity in entities) + { + if (entity.TryGetComponent(out var component) + && entity.Transform.IsMapTransform + && component.CheckClick(coordinates.Position, out var drawDepthClicked)) + { + foundEntities.Add((entity, drawDepthClicked)); + } + } + + if (foundEntities.Count == 0) + return new List(); + + foundEntities.Sort(new ClickableEntityComparer()); + // 0 is the top element. + foundEntities.Reverse(); + return foundEntities.Select(a => a.clicked).ToList(); + } + + internal class ClickableEntityComparer : IComparer<(IEntity clicked, int depth)> + { + public int Compare((IEntity clicked, int depth) x, (IEntity clicked, int depth) y) + { + var val = x.depth.CompareTo(y.depth); + if (val != 0) + { + return val; + } + + var transx = x.clicked.Transform; + var transy = y.clicked.Transform; + return transx.GridPosition.Y.CompareTo(transy.GridPosition.Y); + } + } + + /// + /// Converts a state change event from outside the simulation to inside the simulation. + /// + /// Event data values for a bound key state change. + private void OnKeyBindStateChanged(BoundKeyEventArgs args) + { + var inputSys = _entitySystemManager.GetEntitySystem(); + + var func = args.Function; + var funcId = _inputManager.NetworkBindMap.KeyFunctionID(func); + + var mousePosWorld = _eyeManager.ScreenToWorld(args.PointerLocation); + var entityToClick = GetEntityUnderPosition(mousePosWorld); + var message = new FullInputCmdMessage(_timing.CurTick, funcId, args.State, mousePosWorld, + args.PointerLocation, entityToClick?.Uid ?? EntityUid.Invalid); + + // client side command handlers will always be sent the local player session. + var session = _playerManager.LocalPlayer.Session; + inputSys.HandleInputCommand(session, func, message); + } + } +} diff --git a/Content.Client/State/LobbyState.cs b/Content.Client/State/LobbyState.cs new file mode 100644 index 0000000000..b411316a18 --- /dev/null +++ b/Content.Client/State/LobbyState.cs @@ -0,0 +1,188 @@ +using System; +using System.Linq; +using Content.Client.Interfaces; +using Content.Client.Interfaces.Chat; +using Content.Client.UserInterface; +using Content.Shared.Input; +using Robust.Client.Console; +using Robust.Client.Interfaces; +using Robust.Client.Interfaces.Input; +using Robust.Client.Interfaces.ResourceManagement; +using Robust.Client.Interfaces.UserInterface; +using Robust.Client.Player; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Input; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Robust.Shared.ViewVariables; + +namespace Content.Client.State +{ + public class LobbyState : StateBase + { +#pragma warning disable 649 + [Dependency] private readonly IBaseClient _baseClient; + [Dependency] private readonly IClientConsole _console; + [Dependency] private readonly IChatManager _chatManager; + [Dependency] private readonly IInputManager _inputManager; + [Dependency] private readonly IEntityManager _entityManager; + [Dependency] private readonly IPlayerManager _playerManager; + [Dependency] private readonly IResourceCache _resourceCache; + [Dependency] private readonly IClientGameTicker _clientGameTicker; + [Dependency] private readonly IPrototypeManager _prototypeManager; + [Dependency] private readonly IUserInterfaceManager _userInterfaceManager; + [Dependency] private readonly IClientPreferencesManager _preferencesManager; +#pragma warning restore 649 + + [ViewVariables] private CharacterSetupGui _characterSetup; + [ViewVariables] private LobbyGui _lobby; + + public override void Startup() + { + _characterSetup = new CharacterSetupGui(_entityManager, _resourceCache, _preferencesManager, + _prototypeManager); + LayoutContainer.SetAnchorPreset(_characterSetup, LayoutContainer.LayoutPreset.Wide); + _characterSetup.CloseButton.OnPressed += args => + { + _characterSetup.Save(); + _lobby.CharacterPreview.UpdateUI(); + _userInterfaceManager.StateRoot.AddChild(_lobby); + _userInterfaceManager.StateRoot.RemoveChild(_characterSetup); + }; + + _lobby = new LobbyGui(_entityManager, _resourceCache, _preferencesManager); + _userInterfaceManager.StateRoot.AddChild(_lobby); + + LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide); + + _chatManager.SetChatBox(_lobby.Chat); + _lobby.Chat.DefaultChatFormat = "ooc \"{0}\""; + + _lobby.ServerName.Text = _baseClient.GameInfo.ServerName; + + _inputManager.SetInputCommand(ContentKeyFunctions.FocusChat, + InputCmdHandler.FromDelegate(s => GameScreen.FocusChat(_lobby.Chat))); + + UpdateLobbyUi(); + + _lobby.CharacterPreview.CharacterSetupButton.OnPressed += args => + { + SetReady(false); + _userInterfaceManager.StateRoot.RemoveChild(_lobby); + _userInterfaceManager.StateRoot.AddChild(_characterSetup); + }; + + _lobby.ObserveButton.OnPressed += args => _console.ProcessCommand("observe"); + _lobby.ReadyButton.OnPressed += args => + { + if (!_clientGameTicker.IsGameStarted) + { + return; + } + + _console.ProcessCommand("joingame"); + }; + + _lobby.ReadyButton.OnToggled += args => + { + SetReady(args.Pressed); + }; + + _lobby.LeaveButton.OnPressed += args => _console.ProcessCommand("disconnect"); + + UpdatePlayerList(); + + _playerManager.PlayerListUpdated += PlayerManagerOnPlayerListUpdated; + _clientGameTicker.InfoBlobUpdated += UpdateLobbyUi; + _clientGameTicker.LobbyStatusUpdated += UpdateLobbyUi; + } + + public override void Shutdown() + { + _playerManager.PlayerListUpdated -= PlayerManagerOnPlayerListUpdated; + _clientGameTicker.InfoBlobUpdated -= UpdateLobbyUi; + _clientGameTicker.LobbyStatusUpdated -= UpdateLobbyUi; + + _lobby.Dispose(); + _characterSetup.Dispose(); + } + + public override void FrameUpdate(FrameEventArgs e) + { + if (_clientGameTicker.IsGameStarted) + { + _lobby.StartTime.Text = ""; + return; + } + + string text; + var difference = _clientGameTicker.StartTime - DateTime.UtcNow; + if (difference.Ticks < 0) + { + if (difference.TotalSeconds < -5) + { + text = Loc.GetString("Right Now?"); + } + else + { + text = Loc.GetString("Right Now"); + } + } + else + { + text = $"{(int) Math.Floor(difference.TotalMinutes)}:{difference.Seconds:D2}"; + } + + _lobby.StartTime.Text = Loc.GetString("Round Starts In: {0}", text); + } + + private void PlayerManagerOnPlayerListUpdated(object sender, EventArgs e) => UpdatePlayerList(); + + private void UpdateLobbyUi() + { + if (_lobby == null) + { + return; + } + + if (_clientGameTicker.IsGameStarted) + { + _lobby.ReadyButton.Text = Loc.GetString("Join"); + _lobby.ReadyButton.ToggleMode = false; + _lobby.ReadyButton.Pressed = false; + } + else + { + _lobby.StartTime.Text = ""; + _lobby.ReadyButton.Text = Loc.GetString("Ready Up"); + _lobby.ReadyButton.ToggleMode = true; + _lobby.ReadyButton.Pressed = _clientGameTicker.AreWeReady; + } + + _lobby.ServerInfo.SetInfoBlob(_clientGameTicker.ServerInfoBlob); + } + + private void UpdatePlayerList() + { + _lobby.OnlinePlayerItemList.Clear(); + + foreach (var session in _playerManager.Sessions.OrderBy(s => s.Name)) + { + _lobby.OnlinePlayerItemList.AddItem(session.Name); + } + } + + private void SetReady(bool newReady) + { + if (_clientGameTicker.IsGameStarted) + { + return; + } + + _console.ProcessCommand($"toggleready {newReady}"); + } + } +} diff --git a/Content.Client/State/StateBase.cs b/Content.Client/State/StateBase.cs new file mode 100644 index 0000000000..1b981efe62 --- /dev/null +++ b/Content.Client/State/StateBase.cs @@ -0,0 +1,32 @@ +using Robust.Client.Interfaces.GameObjects; +using Robust.Client.Interfaces.Placement; +using Robust.Client.Player; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Timing; + +namespace Content.Client.State +{ + public abstract class StateBase : Robust.Client.State.State + { +#pragma warning disable 649 + [Dependency] private readonly IClientEntityManager _entityManager; + [Dependency] private readonly IComponentManager _componentManager; + [Dependency] private readonly IPlayerManager _playerManager; + [Dependency] private readonly IPlacementManager _placementManager; +#pragma warning restore 649 + + public override void Update(FrameEventArgs e) + { + _componentManager.CullRemovedComponents(); + _entityManager.Update(e.DeltaSeconds); + _playerManager.Update(e.DeltaSeconds); + } + + public override void FrameUpdate(FrameEventArgs e) + { + _placementManager.FrameUpdate(e); + _entityManager.FrameUpdate(e.DeltaSeconds); + } + } +} diff --git a/Content.Client/UserInterface/CharacterSetupGui.cs b/Content.Client/UserInterface/CharacterSetupGui.cs index 7e81d7cc7f..ca738faeb3 100644 --- a/Content.Client/UserInterface/CharacterSetupGui.cs +++ b/Content.Client/UserInterface/CharacterSetupGui.cs @@ -28,7 +28,6 @@ namespace Content.Client.UserInterface public readonly Button CloseButton; public CharacterSetupGui(IEntityManager entityManager, - ILocalizationManager localization, IResourceCache resourceCache, IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager) @@ -67,7 +66,7 @@ namespace Content.Client.UserInterface CloseButton = new Button { SizeFlagsHorizontal = SizeFlags.Expand | SizeFlags.ShrinkEnd, - Text = localization.GetString("Save and close"), + Text = Loc.GetString("Save and close"), StyleClasses = {NanoStyle.StyleClassButtonBig} }; @@ -83,7 +82,7 @@ namespace Content.Client.UserInterface { new Label { - Text = localization.GetString("Character Setup"), + Text = Loc.GetString("Character Setup"), StyleClasses = {NanoStyle.StyleClassLabelHeadingBigger}, VAlign = Label.VAlignMode.Center, SizeFlagsHorizontal = SizeFlags.Expand | SizeFlags.ShrinkCenter @@ -149,17 +148,14 @@ namespace Content.Client.UserInterface PanelOverride = new StyleBoxFlat {BackgroundColor = NanoStyle.NanoGold}, CustomMinimumSize = (2, 0) }); - _humanoidProfileEditor = new HumanoidProfileEditor(localization, preferencesManager, prototypeManager); + _humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager); _humanoidProfileEditor.OnProfileChanged += newProfile => { UpdateUI(); }; hBox.AddChild(_humanoidProfileEditor); UpdateUI(); } - public void Save() - { - _humanoidProfileEditor.Save(); - } + public void Save() => _humanoidProfileEditor.Save(); private void UpdateUI() { diff --git a/Content.Client/UserInterface/HumanoidProfileEditor.cs b/Content.Client/UserInterface/HumanoidProfileEditor.cs index 442c5d8aa1..647c4180ad 100644 --- a/Content.Client/UserInterface/HumanoidProfileEditor.cs +++ b/Content.Client/UserInterface/HumanoidProfileEditor.cs @@ -48,8 +48,7 @@ namespace Content.Client.UserInterface public HumanoidCharacterProfile Profile; public event Action OnProfileChanged; - public HumanoidProfileEditor(ILocalizationManager localization, - IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager) + public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager) { _random = IoCManager.Resolve(); Profile = (HumanoidCharacterProfile) preferencesManager.Preferences.SelectedCharacter; @@ -83,7 +82,7 @@ namespace Content.Client.UserInterface var panel = HighlightedContainer(); var randomizeEverythingButton = new Button { - Text = localization.GetString("Randomize everything") + Text = Loc.GetString("Randomize everything") }; randomizeEverythingButton.OnPressed += args => { RandomizeEverything(); }; panel.AddChild(randomizeEverythingButton); @@ -100,7 +99,7 @@ namespace Content.Client.UserInterface { SizeFlagsVertical = SizeFlags.FillExpand }; - var nameLabel = new Label {Text = localization.GetString("Name:")}; + var nameLabel = new Label {Text = Loc.GetString("Name:")}; _nameEdit = new LineEdit { CustomMinimumSize = (270, 0), @@ -109,7 +108,7 @@ namespace Content.Client.UserInterface _nameEdit.OnTextChanged += args => { SetName(args.Text); }; var nameRandomButton = new Button { - Text = localization.GetString("Randomize"), + Text = Loc.GetString("Randomize"), }; nameRandomButton.OnPressed += args => RandomizeName(); hBox.AddChild(nameLabel); @@ -143,19 +142,19 @@ namespace Content.Client.UserInterface { var panel = HighlightedContainer(); var hBox = new HBoxContainer(); - var sexLabel = new Label {Text = localization.GetString("Sex:")}; + var sexLabel = new Label {Text = Loc.GetString("Sex:")}; var sexButtonGroup = new ButtonGroup(); _sexMaleButton = new Button { - Text = localization.GetString("Male"), + Text = Loc.GetString("Male"), Group = sexButtonGroup }; _sexMaleButton.OnPressed += args => { SetSex(Sex.Male); }; _sexFemaleButton = new Button { - Text = localization.GetString("Female"), + Text = Loc.GetString("Female"), Group = sexButtonGroup }; _sexFemaleButton.OnPressed += args => { SetSex(Sex.Female); }; @@ -173,7 +172,7 @@ namespace Content.Client.UserInterface { var panel = HighlightedContainer(); var hBox = new HBoxContainer(); - var ageLabel = new Label {Text = localization.GetString("Age:")}; + var ageLabel = new Label {Text = Loc.GetString("Age:")}; _ageEdit = new LineEdit {CustomMinimumSize = (40, 0)}; _ageEdit.OnTextChanged += args => { @@ -332,13 +331,13 @@ namespace Content.Client.UserInterface var hBox = new HBoxContainer(); var importButton = new Button { - Text = localization.GetString("Import"), + Text = Loc.GetString("Import"), Disabled = true, ToolTip = "Not yet implemented!" }; var exportButton = new Button { - Text = localization.GetString("Export"), + Text = Loc.GetString("Export"), Disabled = true, ToolTip = "Not yet implemented!" }; @@ -356,7 +355,7 @@ namespace Content.Client.UserInterface var panel = HighlightedContainer(); _saveButton = new Button { - Text = localization.GetString("Save"), + Text = Loc.GetString("Save"), SizeFlagsHorizontal = SizeFlags.ShrinkCenter }; _saveButton.OnPressed += args => { Save(); }; diff --git a/Content.Client/UserInterface/LobbyCharacterPreviewPanel.cs b/Content.Client/UserInterface/LobbyCharacterPreviewPanel.cs index 6b9ed12a47..98d800445e 100644 --- a/Content.Client/UserInterface/LobbyCharacterPreviewPanel.cs +++ b/Content.Client/UserInterface/LobbyCharacterPreviewPanel.cs @@ -24,7 +24,6 @@ namespace Content.Client.UserInterface private readonly Label _summaryLabel; public LobbyCharacterPreviewPanel(IEntityManager entityManager, - ILocalizationManager localization, IClientPreferencesManager preferencesManager) { _preferencesManager = preferencesManager; @@ -32,12 +31,12 @@ namespace Content.Client.UserInterface var header = new NanoHeading { - Text = localization.GetString("Character setup") + Text = Loc.GetString("Character setup") }; CharacterSetupButton = new Button { - Text = localization.GetString("Customize"), + Text = Loc.GetString("Customize"), SizeFlagsHorizontal = SizeFlags.None }; diff --git a/Content.Client/UserInterface/LobbyGui.cs b/Content.Client/UserInterface/LobbyGui.cs index 5acd10f365..29624b8ced 100644 --- a/Content.Client/UserInterface/LobbyGui.cs +++ b/Content.Client/UserInterface/LobbyGui.cs @@ -24,7 +24,6 @@ namespace Content.Client.UserInterface public LobbyCharacterPreviewPanel CharacterPreview { get; } public LobbyGui(IEntityManager entityManager, - ILocalizationManager localization, IResourceCache resourceCache, IClientPreferencesManager preferencesManager) { @@ -69,7 +68,7 @@ namespace Content.Client.UserInterface { new Label { - Text = localization.GetString("Lobby"), + Text = Loc.GetString("Lobby"), StyleClasses = {NanoStyle.StyleClassLabelHeadingBigger}, /*MarginBottom = 40, MarginLeft = 8,*/ @@ -88,7 +87,7 @@ namespace Content.Client.UserInterface (LeaveButton = new Button { SizeFlagsHorizontal = SizeFlags.ShrinkEnd, - Text = localization.GetString("Leave"), + Text = Loc.GetString("Leave"), StyleClasses = {NanoStyle.StyleClassButtonBig}, //GrowHorizontal = GrowDirection.Begin }) @@ -115,7 +114,6 @@ namespace Content.Client.UserInterface CharacterPreview = new LobbyCharacterPreviewPanel( entityManager, - localization, preferencesManager) { SizeFlagsHorizontal = SizeFlags.None @@ -147,7 +145,7 @@ namespace Content.Client.UserInterface { (ObserveButton = new Button { - Text = localization.GetString("Observe"), + Text = Loc.GetString("Observe"), StyleClasses = {NanoStyle.StyleClassButtonBig} }), (StartTime = new Label @@ -160,7 +158,7 @@ namespace Content.Client.UserInterface (ReadyButton = new Button { ToggleMode = true, - Text = localization.GetString("Ready Up"), + Text = Loc.GetString("Ready Up"), StyleClasses = {NanoStyle.StyleClassButtonBig} }), } @@ -181,7 +179,7 @@ namespace Content.Client.UserInterface { (Chat = new ChatBox { - Input = {PlaceHolder = localization.GetString("Say something!")} + Input = {PlaceHolder = Loc.GetString("Say something!")} }) } }, @@ -201,7 +199,7 @@ namespace Content.Client.UserInterface { new NanoHeading { - Text = localization.GetString("Online Players"), + Text = Loc.GetString("Online Players"), }, new MarginContainer { @@ -217,7 +215,7 @@ namespace Content.Client.UserInterface }, new NanoHeading { - Text = localization.GetString("Server Info"), + Text = Loc.GetString("Server Info"), }, new MarginContainer { @@ -228,7 +226,7 @@ namespace Content.Client.UserInterface MarginBottomOverride = 2, Children = { - (ServerInfo = new ServerInfo(localization)) + (ServerInfo = new ServerInfo()) } }, } diff --git a/Content.Client/UserInterface/ServerInfo.cs b/Content.Client/UserInterface/ServerInfo.cs index 91a95b4b8b..aa87ee6e12 100644 --- a/Content.Client/UserInterface/ServerInfo.cs +++ b/Content.Client/UserInterface/ServerInfo.cs @@ -13,7 +13,7 @@ namespace Content.Client.UserInterface private readonly RichTextLabel _richTextLabel; - public ServerInfo(ILocalizationManager localization) + public ServerInfo() { _richTextLabel = new RichTextLabel { @@ -26,10 +26,10 @@ namespace Content.Client.UserInterface var uriOpener = IoCManager.Resolve(); - var discordButton = new Button {Text = localization.GetString("Join us on Discord!")}; + var discordButton = new Button {Text = Loc.GetString("Join us on Discord!")}; discordButton.OnPressed += args => uriOpener.OpenUri(DiscordUrl); - var websiteButton = new Button {Text = localization.GetString("Website")}; + var websiteButton = new Button {Text = Loc.GetString("Website")}; websiteButton.OnPressed += args => uriOpener.OpenUri(WebsiteUrl); buttons.AddChild(discordButton);