diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj
index d7a601bd7c..4f035dd9a9 100644
--- a/Content.Client/Content.Client.csproj
+++ b/Content.Client/Content.Client.csproj
@@ -76,7 +76,9 @@
+
+
@@ -84,6 +86,7 @@
+
diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs
index add7469e56..14854641ca 100644
--- a/Content.Client/EntryPoint.cs
+++ b/Content.Client/EntryPoint.cs
@@ -4,6 +4,7 @@ using Content.Client.GameObjects.Components.Construction;
using Content.Client.GameObjects.Components.Power;
using Content.Client.GameObjects.Components.SmoothWalling;
using Content.Client.GameObjects.Components.Storage;
+using Content.Client.GameTicking;
using Content.Client.Input;
using Content.Client.Interfaces;
using Content.Client.Interfaces.GameObjects;
@@ -77,6 +78,7 @@ namespace Content.Client
IoCManager.Register();
IoCManager.Register();
+ IoCManager.Register();
IoCManager.BuildGraph();
}
@@ -89,6 +91,7 @@ namespace Content.Client
ContentContexts.SetupContexts(inputMan.Contexts);
IoCManager.Resolve().Initialize();
+ IoCManager.Resolve().Initialize();
}
public override void Update(AssemblyLoader.UpdateLevel level, float frameTime)
@@ -98,7 +101,9 @@ namespace Content.Client
switch (level)
{
case AssemblyLoader.UpdateLevel.FramePreEngine:
- IoCManager.Resolve().FrameUpdate(new RenderFrameEventArgs(frameTime));
+ var renderFrameEventArgs = new RenderFrameEventArgs(frameTime);
+ IoCManager.Resolve().FrameUpdate(renderFrameEventArgs);
+ IoCManager.Resolve().FrameUpdate(renderFrameEventArgs);
break;
}
}
diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs
new file mode 100644
index 0000000000..f0da2d47eb
--- /dev/null
+++ b/Content.Client/GameTicking/ClientGameTicker.cs
@@ -0,0 +1,228 @@
+using System;
+using Content.Client.Interfaces;
+using Content.Client.UserInterface;
+using Content.Shared;
+using SS14.Client;
+using SS14.Client.Console;
+using SS14.Client.Interfaces;
+using SS14.Client.Interfaces.Input;
+using SS14.Client.Interfaces.UserInterface;
+using SS14.Client.UserInterface.CustomControls;
+using SS14.Shared.Input;
+using SS14.Shared.Interfaces.Network;
+using SS14.Shared.IoC;
+using SS14.Shared.Utility;
+using SS14.Shared.ViewVariables;
+
+namespace Content.Client.GameTicking
+{
+ public class ClientGameTicker : SharedGameTicker, IClientGameTicker
+ {
+#pragma warning disable 649
+ [Dependency] private IClientNetManager _netManager;
+ [Dependency] private IUserInterfaceManager _userInterfaceManager;
+ [Dependency] private IClientChatConsole _chatConsole;
+ [Dependency] private IInputManager _inputManager;
+ [Dependency] private IBaseClient _baseClient;
+#pragma warning restore 649
+
+ [ViewVariables] private bool _areWeReady;
+ [ViewVariables] private bool _initialized;
+ [ViewVariables] private TickerState _tickerState;
+ [ViewVariables] private Chatbox _gameChat;
+ [ViewVariables] private LobbyGui _lobby;
+ [ViewVariables] private bool _gameStarted;
+ [ViewVariables] private DateTime _startTime;
+
+ public void Initialize()
+ {
+ DebugTools.Assert(!_initialized);
+
+ _netManager.RegisterNetMessage(nameof(MsgTickerJoinLobby), _joinLobby);
+ _netManager.RegisterNetMessage(nameof(MsgTickerJoinGame), _joinGame);
+ _netManager.RegisterNetMessage(nameof(MsgTickerLobbyStatus), _lobbyStatus);
+
+ _baseClient.RunLevelChanged += BaseClientOnRunLevelChanged;
+
+ _initialized = true;
+ }
+
+ private void BaseClientOnRunLevelChanged(object sender, RunLevelChangedEventArgs e)
+ {
+ if (e.NewLevel != ClientRunLevel.Initialize)
+ {
+ return;
+ }
+
+ _tickerState = TickerState.Unset;
+ _lobby?.Dispose();
+ _lobby = null;
+ _gameChat?.Dispose();
+ _gameChat = null;
+ }
+
+ public void FrameUpdate(RenderFrameEventArgs renderFrameEventArgs)
+ {
+ 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 = "Right Now?";
+ }
+ else
+ {
+ text = "Right Now";
+ }
+ }
+ else
+ {
+ text = $"{(int) Math.Floor(difference.TotalMinutes)}:{difference.Seconds:D2}";
+ }
+
+ _lobby.StartTime.Text = "Round Starts In: " + text;
+ }
+
+ private void _lobbyStatus(MsgTickerLobbyStatus message)
+ {
+ _startTime = message.StartTime;
+ _gameStarted = message.IsRoundStarted;
+ _areWeReady = message.YouAreReady;
+
+ _updateLobbyUi();
+ }
+
+ private void _updateLobbyUi()
+ {
+ if (_lobby == null)
+ {
+ return;
+ }
+
+ if (_gameStarted)
+ {
+ _lobby.ReadyButton.Text = "Join";
+ _lobby.ReadyButton.ToggleMode = false;
+ _lobby.ReadyButton.Pressed = false;
+ }
+ else
+ {
+ _lobby.StartTime.Text = "";
+ _lobby.ReadyButton.Text = "Ready Up";
+ _lobby.ReadyButton.ToggleMode = true;
+ _lobby.ReadyButton.Pressed = _areWeReady;
+ }
+ }
+
+ private void _joinLobby(MsgTickerJoinLobby message)
+ {
+ if (_tickerState == TickerState.InLobby)
+ {
+ return;
+ }
+
+ if (_gameChat != null)
+ {
+ _gameChat.TextSubmitted -= _chatConsole.ParseChatMessage;
+ _chatConsole.AddString -= _gameChat.AddLine;
+ _gameChat.Dispose();
+ _gameChat = null;
+ }
+
+ _tickerState = TickerState.InLobby;
+
+ _lobby = new LobbyGui();
+ _userInterfaceManager.StateRoot.AddChild(_lobby);
+
+ _lobby.Chat.TextSubmitted += _chatConsole.ParseChatMessage;
+ _chatConsole.AddString += _lobby.Chat.AddLine;
+ _lobby.Chat.DefaultChatFormat = "ooc \"{0}\"";
+
+ _lobby.ServerName.Text = _baseClient.GameInfo.ServerName;
+
+ _inputManager.SetInputCommand(EngineKeyFunctions.FocusChat,
+ InputCmdHandler.FromDelegate(session => { _lobby.Chat.Input.GrabFocus(); }));
+
+ _updateLobbyUi();
+
+ _lobby.ObserveButton.OnPressed += args => { _chatConsole.ProcessCommand("observe"); };
+
+ _lobby.ReadyButton.OnPressed += args =>
+ {
+ if (!_gameStarted)
+ {
+ return;
+ }
+
+ _chatConsole.ProcessCommand("joingame");
+ };
+
+ _lobby.ReadyButton.OnToggled += args =>
+ {
+ if (_gameStarted)
+ {
+ return;
+ }
+
+ _chatConsole.ProcessCommand($"toggleready {args.Pressed}");
+ };
+
+ _lobby.LeaveButton.OnPressed += args => _chatConsole.ProcessCommand("disconnect");
+ }
+
+ private void _joinGame(MsgTickerJoinGame message)
+ {
+ if (_tickerState == TickerState.InGame)
+ {
+ return;
+ }
+
+ _tickerState = TickerState.InGame;
+
+ if (_lobby != null)
+ {
+ _lobby.Chat.TextSubmitted -= _chatConsole.ParseChatMessage;
+ _chatConsole.AddString -= _lobby.Chat.AddLine;
+ _lobby.Dispose();
+ _lobby = null;
+ }
+
+ _inputManager.SetInputCommand(EngineKeyFunctions.FocusChat,
+ InputCmdHandler.FromDelegate(session => { _gameChat.Input.GrabFocus(); }));
+
+ _gameChat = new Chatbox();
+ _userInterfaceManager.StateRoot.AddChild(_gameChat);
+ _gameChat.TextSubmitted += _chatConsole.ParseChatMessage;
+ _chatConsole.AddString += _gameChat.AddLine;
+ _gameChat.DefaultChatFormat = "say \"{0}\"";
+ }
+
+ 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
+ }
+ }
+}
diff --git a/Content.Client/Interfaces/IClientGameTicker.cs b/Content.Client/Interfaces/IClientGameTicker.cs
new file mode 100644
index 0000000000..a612b28b8a
--- /dev/null
+++ b/Content.Client/Interfaces/IClientGameTicker.cs
@@ -0,0 +1,10 @@
+using SS14.Client;
+
+namespace Content.Client.Interfaces
+{
+ public interface IClientGameTicker
+ {
+ void Initialize();
+ void FrameUpdate(RenderFrameEventArgs renderFrameEventArgs);
+ }
+}
diff --git a/Content.Client/UserInterface/LobbyGui.cs b/Content.Client/UserInterface/LobbyGui.cs
new file mode 100644
index 0000000000..9fe5413409
--- /dev/null
+++ b/Content.Client/UserInterface/LobbyGui.cs
@@ -0,0 +1,35 @@
+using SS14.Client.UserInterface;
+using SS14.Client.UserInterface.Controls;
+using SS14.Client.UserInterface.CustomControls;
+using SS14.Shared.Utility;
+
+namespace Content.Client.UserInterface
+{
+ public class LobbyGui : Control
+ {
+ protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Lobby/Lobby.tscn");
+
+ public Label ServerName => GetChild