diff --git a/CHANGELOG b/CHANGELOG index 54ec1af64e..b14683d7ff 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -23,6 +23,7 @@ NEW: Order lines are now shown on unit selection. Fixed chat synchronization in replays. Added a combined shroud view of every player to the replay viewer and spectator mode. + Added pause, slowdown, play and fastforward buttons for replays. Fixed the game sometimes crashing when deploying and activating the guard cursor at the same time. Build time is now set when an item reaches the front of a queue, instead of immediately when queued. The attack cursor now changes if the target is out of range. diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index f0b61be1e6..1f1499411f 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -73,8 +73,9 @@ namespace OpenRA public static int RenderFrame = 0; public static int NetFrameNumber { get { return orderManager.NetFrameNumber; } } public static int LocalTick { get { return orderManager.LocalFrameNumber; } } - public const int NetTickScale = 3; // 120ms net tick for 40ms local tick + public const int NetTickScale = 3; // 120 ms net tick for 40 ms local tick public const int Timestep = 40; + public const int TimestepJankThreshold = 250; // Don't catch up for delays larger than 250ms public static event Action ConnectionStateChanged = _ => { }; static ConnectionState lastConnectionState = ConnectionState.PreConnecting; @@ -163,14 +164,30 @@ namespace OpenRA static void TickInner(OrderManager orderManager) { - int t = Environment.TickCount; - int dt = t - orderManager.LastTickTime; - if (dt >= Settings.Game.Timestep) + var tick = Environment.TickCount; + + var world = orderManager.world; + var uiTickDelta = tick - Ui.LastTickTime; + if (uiTickDelta >= Timestep) + { + Ui.LastTickTime += Timestep; + Sync.CheckSyncUnchanged(world, Ui.Tick); + cursorFrame += 0.5f; + } + + var worldTimestep = world == null ? Timestep : world.Timestep; + var worldTickDelta = (tick - orderManager.LastTickTime); + if (worldTimestep != 0 && worldTickDelta >= worldTimestep) using (new PerfSample("tick_time")) { - orderManager.LastTickTime += Settings.Game.Timestep; - Ui.Tick(); - var world = orderManager.world; + // Tick the world to advance the world time to match real time: + // If dt < TickJankThreshold then we should try and catch up by repeatedly ticking + // If dt >= TickJankThreshold then we should accept the jank and progress at the normal rate + // dt is rounded down to an integer tick count in order to preserve fractional tick components. + + var integralTickTimestep = (worldTickDelta / worldTimestep) * worldTimestep; + orderManager.LastTickTime += integralTickTimestep >= TimestepJankThreshold ? integralTickTimestep : worldTimestep; + if (orderManager.GameStarted) ++Viewport.TicksSinceLastMove; @@ -205,8 +222,6 @@ namespace OpenRA orderManager.LastTickTime = Environment.TickCount; Sync.CheckSyncUnchanged(world, () => world.TickRender(worldRenderer)); - - cursorFrame += 0.5f; } } } diff --git a/OpenRA.Game/GameRules/Settings.cs b/OpenRA.Game/GameRules/Settings.cs index 15bf950ebf..4295333806 100644 --- a/OpenRA.Game/GameRules/Settings.cs +++ b/OpenRA.Game/GameRules/Settings.cs @@ -135,9 +135,6 @@ namespace OpenRA.GameRules public bool AlwaysShowStatusBars = false; public bool TeamHealthColors = false; - // Internal game settings - public int Timestep = 40; - public bool AllowDownloading = true; public string[] MapRepositories = { "http://resource.openra.net/map/", "http://resource.ihptru.net:8080/map/" }; } diff --git a/OpenRA.Game/Widgets/Widget.cs b/OpenRA.Game/Widgets/Widget.cs index b66990fb39..ab365f8af8 100644 --- a/OpenRA.Game/Widgets/Widget.cs +++ b/OpenRA.Game/Widgets/Widget.cs @@ -21,6 +21,8 @@ namespace OpenRA.Widgets { public static Widget Root = new ContainerWidget(); + public static int LastTickTime = Environment.TickCount; + static Stack WindowList = new Stack(); public static Widget MouseFocusWidget; diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index d9201f5aa9..8aca6522a3 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -66,9 +66,14 @@ namespace OpenRA } } + public bool IsReplay + { + get { return orderManager.Connection is ReplayConnection; } + } + public void SetLocalPlayer(string pr) { - if (orderManager.Connection is ReplayConnection) + if (IsReplay) return; LocalPlayer = Players.FirstOrDefault(p => p.InternalName == pr); diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 8a83a0958c..1dc2c1ffbf 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -479,9 +479,9 @@ - + diff --git a/OpenRA.Mods.RA/Widgets/Logic/GameTimerLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/GameTimerLogic.cs index a2e6a87b1f..ecc68f18fd 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/GameTimerLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/GameTimerLogic.cs @@ -26,9 +26,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic var status = widget.GetOrNull("GAME_TIMER_STATUS"); if (status != null) { + var startTick = Ui.LastTickTime; // Blink the status line status.IsVisible = () => (world.Paused || world.Timestep != Game.Timestep) - && orderManager.LocalFrameNumber / 25 % 2 == 0; + && (Ui.LastTickTime - startTick) / 1000 % 2 == 0; status.GetText = () => { diff --git a/OpenRA.Mods.RA/Widgets/Logic/ReplayControlBarLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ReplayControlBarLogic.cs new file mode 100644 index 0000000000..a2530d9302 --- /dev/null +++ b/OpenRA.Mods.RA/Widgets/Logic/ReplayControlBarLogic.cs @@ -0,0 +1,51 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Linq; +using OpenRA.Widgets; + +namespace OpenRA.Mods.RA.Widgets.Logic +{ + public class ReplayControlBarLogic + { + + [ObjectCreator.UseCtor] + public ReplayControlBarLogic(Widget widget, World world) + { + if (world.IsReplay) + { + var container = widget.Get("REPLAY_PLAYER"); + + var background = widget.Parent.GetOrNull("OBSERVER_CONTROL_BG"); + if (background != null) + background.Bounds.Height += container.Bounds.Height; + + container.Visible = true; + + var pauseButton = widget.Get("BUTTON_PAUSE"); + pauseButton.IsHighlighted = () => world.Timestep == 0; + pauseButton.OnClick = () => world.Timestep = 0; + + var slowButton = widget.Get("BUTTON_SLOW"); + slowButton.IsHighlighted = () => world.Timestep > Game.Timestep; + slowButton.OnClick = () => world.Timestep = Game.Timestep * 2; + + var normalSpeedButton = widget.Get("BUTTON_NORMALSPEED"); + normalSpeedButton.IsHighlighted = () => world.Timestep == Game.Timestep; + normalSpeedButton.OnClick = () => world.Timestep = Game.Timestep; + + var fastforwardButton = widget.Get("BUTTON_FASTFORWARD"); + fastforwardButton.IsHighlighted = () => world.Timestep == 1; + fastforwardButton.OnClick = () => world.Timestep = 1; + } + } + } +} diff --git a/artsrc/cnc/chrome.svg b/artsrc/cnc/chrome.svg index 7f653feeee..19a978210e 100644 --- a/artsrc/cnc/chrome.svg +++ b/artsrc/cnc/chrome.svg @@ -13,9 +13,9 @@ height="512" id="svg2" version="1.1" - inkscape:version="0.48.2 r9819" + inkscape:version="0.48.4 r9939" sodipodi:docname="chrome.svg" - inkscape:export-filename="/Users/paul/src/OpenRA/mods/cnc/uibits/chrome.png" + inkscape:export-filename="/home/matthias/Projekte/OpenRA/mods/cnc/uibits/chrome.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" enable-background="new"> @@ -100,16 +100,16 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="5.6568539" - inkscape:cx="371.83537" - inkscape:cy="425.37964" + inkscape:cx="424.27411" + inkscape:cy="420.32669" inkscape:document-units="px" - inkscape:current-layer="svg2" + inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1386" - inkscape:window-height="856" - inkscape:window-x="54" - inkscape:window-y="22" - inkscape:window-maximized="0" + inkscape:window-width="1920" + inkscape:window-height="1060" + inkscape:window-x="1364" + inkscape:window-y="-3" + inkscape:window-maximized="1" showguides="true" inkscape:guide-bbox="true"> image/svg+xml - + @@ -1355,6 +1355,21 @@ id="path3208" d="m 416,596.3622 -4,4 0,0 4,4 z" style="fill:#ffffff;fill-opacity:1;stroke:none" /> + + + +