From 257c043e5897ae3523b4e58824ac3dd8a97eb6c3 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 3 Sep 2015 18:06:56 +0100 Subject: [PATCH 1/3] Add Timestep property to GlobalSettings. --- OpenRA.Game/Game.cs | 2 +- OpenRA.Game/Network/Session.cs | 1 + OpenRA.Game/World.cs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index efaaaf0f39..8441202676 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -147,7 +147,7 @@ namespace OpenRA using (new PerfTimer("PrepareMap")) map = ModData.PrepareMap(mapUID); using (new PerfTimer("NewWorld")) - OrderManager.World = new World(map, OrderManager, type) { Timestep = Timestep }; + OrderManager.World = new World(map, OrderManager, type); worldRenderer = new WorldRenderer(OrderManager.World); diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 5d33cf98bc..9c7c6c5d22 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -172,6 +172,7 @@ namespace OpenRA.Network { public string ServerName; public string Map; + public int Timestep = 40; public int OrderLatency = 3; // net tick frames (x 120 = ms) public int RandomSeed = 0; public bool FragileAlliances = false; // Allow diplomatic stance changes after game start. diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 04140c7b7d..98448eb2f8 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -144,6 +144,7 @@ namespace OpenRA OrderManager = orderManager; orderGenerator = new UnitOrderGenerator(); Map = map; + Timestep = orderManager.LobbyInfo.GlobalSettings.Timestep; TileSet = map.Rules.TileSets[Map.Tileset]; SharedRandom = new MersenneTwister(orderManager.LobbyInfo.GlobalSettings.RandomSeed); From 301b698c81ead18a72c7c01f6c26f0c3fe624177 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 3 Sep 2015 21:40:45 +0100 Subject: [PATCH 2/3] Add game speed dropdown to the lobby. --- OpenRA.Game/GameSpeed.cs | 40 +++++++++++++++++++ OpenRA.Game/Network/Session.cs | 1 + OpenRA.Game/OpenRA.Game.csproj | 1 + .../ServerTraits/LobbyCommands.cs | 28 +++++++++++++ .../Widgets/Logic/Ingame/GameTimerLogic.cs | 4 +- .../Widgets/Logic/Lobby/LobbyLogic.cs | 38 ++++++++++++++++++ mods/cnc/chrome/lobby-dialogs.yaml | 40 ++++++++++++------- mods/cnc/mod.yaml | 20 +++++++++- mods/d2k/chrome/lobby-dialogs.yaml | 39 ++++++++++++------ mods/d2k/mod.yaml | 18 +++++++++ mods/ra/chrome/lobby-dialogs.yaml | 39 ++++++++++++------ mods/ra/mod.yaml | 18 +++++++++ mods/ts/mod.yaml | 18 +++++++++ 13 files changed, 261 insertions(+), 43 deletions(-) create mode 100644 OpenRA.Game/GameSpeed.cs diff --git a/OpenRA.Game/GameSpeed.cs b/OpenRA.Game/GameSpeed.cs new file mode 100644 index 0000000000..24e7eebac8 --- /dev/null +++ b/OpenRA.Game/GameSpeed.cs @@ -0,0 +1,40 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Collections.Generic; +using System.Linq; +using OpenRA.Graphics; + +namespace OpenRA +{ + public class GameSpeed + { + [Translate] + public readonly string Name = "Default"; + public readonly int Timestep = 40; + public readonly int OrderLatency = 3; + } + + public class GameSpeeds : IGlobalModData + { + [FieldLoader.LoadUsing("LoadSpeeds")] + public readonly Dictionary Speeds; + + static object LoadSpeeds(MiniYaml y) + { + var ret = new Dictionary(); + foreach (var node in y.Nodes) + ret.Add(node.Key, FieldLoader.Load(node.Value)); + + return ret; + } + } +} diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 9c7c6c5d22..d4a09185a0 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -188,6 +188,7 @@ namespace OpenRA.Network public int StartingCash = 5000; public string TechLevel = "none"; public string StartingUnitsClass = "none"; + public string GameSpeedType = "default"; public bool ShortGame = true; public bool AllowVersionMismatch; public string GameUid; diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index bb9a09f303..e8de19d10a 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -243,6 +243,7 @@ + diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index 5799a5e04c..186a497b72 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -675,6 +675,34 @@ namespace OpenRA.Mods.Common.Server return true; } }, + { "gamespeed", + s => + { + if (!client.IsAdmin) + { + server.SendOrderTo(conn, "Message", "Only the host can set that option."); + return true; + } + + var gameSpeeds = Game.ModData.Manifest.Get(); + + GameSpeed speed; + if (!gameSpeeds.Speeds.TryGetValue(s, out speed)) + { + server.SendOrderTo(conn, "Message", "Invalid game speed selected."); + return true; + } + + server.LobbyInfo.GlobalSettings.GameSpeedType = s; + server.LobbyInfo.GlobalSettings.Timestep = speed.Timestep; + server.LobbyInfo.GlobalSettings.OrderLatency = speed.OrderLatency; + + server.SyncLobbyInfo(); + server.SendMessage("{0} changed Game Speed to {1}.".F(client.Name, speed.Name)); + + return true; + } + }, { "kick", s => { diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs index 5fbdb7fab4..4de3ed228b 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs @@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var status = widget.GetOrNull("GAME_TIMER_STATUS"); var startTick = Ui.LastTickTime; - Func shouldShowStatus = () => (world.Paused || world.Timestep != Game.Timestep) + Func shouldShowStatus = () => (world.Paused || world.Timestep != world.LobbyInfo.GlobalSettings.Timestep) && (Ui.LastTickTime - startTick) / 1000 % 2 == 0; Func statusText = () => @@ -34,7 +34,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (world.Timestep == 1) return "Max Speed"; - return "{0}% Speed".F(Game.Timestep * 100 / world.Timestep); + return "{0}% Speed".F(world.LobbyInfo.GlobalSettings.Timestep * 100 / world.Timestep); }; if (timer != null) diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs index c22ce9ca99..db5a852100 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs @@ -490,6 +490,44 @@ namespace OpenRA.Mods.Common.Widgets.Logic }; } + var gameSpeed = optionsBin.GetOrNull("GAMESPEED_DROPDOWNBUTTON"); + if (gameSpeed != null) + { + var speeds = Game.ModData.Manifest.Get().Speeds; + + gameSpeed.IsDisabled = () => Map.Status != MapStatus.Available || configurationDisabled(); + gameSpeed.GetText = () => + { + if (Map.Status != MapStatus.Available) + return "Not Available"; + + GameSpeed speed; + if (!speeds.TryGetValue(orderManager.LobbyInfo.GlobalSettings.GameSpeedType, out speed)) + return "Unknown"; + + return speed.Name; + }; + + gameSpeed.OnMouseDown = _ => + { + var options = speeds.Select(s => new DropDownOption + { + Title = s.Value.Name, + IsSelected = () => orderManager.LobbyInfo.GlobalSettings.GameSpeedType == s.Key, + OnClick = () => orderManager.IssueOrder(Order.Command("gamespeed {0}".F(s.Key))) + }); + + Func setupItem = (option, template) => + { + var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick); + item.Get("LABEL").GetText = () => option.Title; + return item; + }; + + gameSpeed.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem); + }; + } + var exploredMap = optionsBin.GetOrNull("EXPLORED_MAP_CHECKBOX"); if (exploredMap != null) { diff --git a/mods/cnc/chrome/lobby-dialogs.yaml b/mods/cnc/chrome/lobby-dialogs.yaml index c31a35e178..183b8494bc 100644 --- a/mods/cnc/chrome/lobby-dialogs.yaml +++ b/mods/cnc/chrome/lobby-dialogs.yaml @@ -175,19 +175,6 @@ Background@LOBBY_OPTIONS_BIN: Width: 140 Height: 25 Font: Regular - Label@DIFFICULTY_DESC: - X: PARENT_RIGHT - WIDTH - 135 - Y: 107 - Width: 120 - Height: 25 - Text: Mission Difficulty: - Align: Right - DropDownButton@DIFFICULTY_DROPDOWNBUTTON: - X: PARENT_RIGHT - WIDTH + 10 - Y: 107 - Width: 140 - Height: 25 - Font: Regular DropDownButton@TECHLEVEL_DROPDOWNBUTTON: X: 85 Y: 112 @@ -201,7 +188,32 @@ Background@LOBBY_OPTIONS_BIN: Height: 25 Text: Tech Level: Align: Right - + Label@GAMESPEED_DESC: + X: PARENT_RIGHT - WIDTH - 135 + Y: 112 + Width: 120 + Height: 25 + Text: Game Speed: + Align: Right + DropDownButton@GAMESPEED_DROPDOWNBUTTON: + X: PARENT_RIGHT - WIDTH + 10 + Y: 112 + Width: 140 + Height: 25 + Font: Regular + Label@DIFFICULTY_DESC: + X: PARENT_RIGHT - WIDTH - 135 + Y: 152 + Width: 120 + Height: 25 + Text: Mission Difficulty: + Align: Right + DropDownButton@DIFFICULTY_DROPDOWNBUTTON: + X: PARENT_RIGHT - WIDTH + 10 + Y: 152 + Width: 140 + Height: 25 + Font: Regular Background@FORCE_START_DIALOG: X: 15 Y: 30 diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index 9c34f8b63e..4a7f7e5a0b 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -213,4 +213,22 @@ SpriteSequenceFormat: TilesetSpecificSpriteSequence WINTER: .win SNOW: .sno DESERT: .des - JUNGLE: .jun \ No newline at end of file + JUNGLE: .jun + +GameSpeeds: + slower: + Name: Slower + Timestep: 50 + OrderLatency: 3 + default: + Name: Default + Timestep: 40 + OrderLatency: 3 + faster: + Name: Faster + Timestep: 30 + OrderLatency: 4 + fastest: + Name: Fastest + Timestep: 20 + OrderLatency: 6 diff --git a/mods/d2k/chrome/lobby-dialogs.yaml b/mods/d2k/chrome/lobby-dialogs.yaml index 5a57019cd7..614777b41a 100644 --- a/mods/d2k/chrome/lobby-dialogs.yaml +++ b/mods/d2k/chrome/lobby-dialogs.yaml @@ -151,6 +151,19 @@ Background@LOBBY_OPTIONS_BIN: Width: 140 Height: 20 Text: Debug Menu + Label@DIFFICULTY_DESC: + X: PARENT_RIGHT - WIDTH - 145 + Y: 70 + Width: 120 + Height: 25 + Text: Mission Difficulty: + Align: Right + DropDownButton@DIFFICULTY_DROPDOWNBUTTON: + X: PARENT_RIGHT - WIDTH + Y: 70 + Width: 140 + Height: 25 + Font: Regular Label@STARTINGCASH_DESC: Y: 110 Width: 80 @@ -177,19 +190,6 @@ Background@LOBBY_OPTIONS_BIN: Width: 140 Height: 25 Font: Regular - Label@DIFFICULTY_DESC: - X: PARENT_RIGHT - WIDTH - 145 - Y: 150 - Width: 120 - Height: 25 - Text: Mission Difficulty: - Align: Right - DropDownButton@DIFFICULTY_DROPDOWNBUTTON: - X: PARENT_RIGHT - WIDTH - Y: 150 - Width: 140 - Height: 25 - Font: Regular DropDownButton@TECHLEVEL_DROPDOWNBUTTON: X: 85 Y: 150 @@ -203,6 +203,19 @@ Background@LOBBY_OPTIONS_BIN: Height: 25 Text: Tech Level: Align: Right + Label@GAMESPEED_DESC: + X: PARENT_RIGHT - WIDTH - 145 + Y: 150 + Width: 120 + Height: 25 + Text: Game Speed: + Align: Right + DropDownButton@GAMESPEED_DROPDOWNBUTTON: + X: PARENT_RIGHT - WIDTH + Y: 150 + Width: 140 + Height: 25 + Font: Regular Background@FORCE_START_DIALOG: X: 20 diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index 554bbdff1a..b638e3b2a5 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -193,3 +193,21 @@ SupportsMapsFrom: d2k SpriteFormats: R8, ShpTD, TmpRA SpriteSequenceFormat: DefaultSpriteSequence + +GameSpeeds: + slower: + Name: Slower + Timestep: 50 + OrderLatency: 3 + default: + Name: Default + Timestep: 40 + OrderLatency: 3 + faster: + Name: Faster + Timestep: 30 + OrderLatency: 4 + fastest: + Name: Fastest + Timestep: 20 + OrderLatency: 6 diff --git a/mods/ra/chrome/lobby-dialogs.yaml b/mods/ra/chrome/lobby-dialogs.yaml index cbc1dfd27d..efa06a8a20 100644 --- a/mods/ra/chrome/lobby-dialogs.yaml +++ b/mods/ra/chrome/lobby-dialogs.yaml @@ -145,6 +145,19 @@ Background@LOBBY_OPTIONS_BIN: Width: 140 Height: 20 Text: Debug Menu + Label@DIFFICULTY_DESC: + X: PARENT_RIGHT - WIDTH - 145 + Y: 70 + Width: 120 + Height: 25 + Text: Mission Difficulty: + Align: Right + DropDownButton@DIFFICULTY_DROPDOWNBUTTON: + X: PARENT_RIGHT - WIDTH + Y: 70 + Width: 140 + Height: 25 + Font: Regular Label@STARTINGCASH_DESC: Y: 110 Width: 80 @@ -171,19 +184,6 @@ Background@LOBBY_OPTIONS_BIN: Width: 140 Height: 25 Font: Regular - Label@DIFFICULTY_DESC: - X: PARENT_RIGHT - WIDTH - 145 - Y: 150 - Width: 120 - Height: 25 - Text: Mission Difficulty: - Align: Right - DropDownButton@DIFFICULTY_DROPDOWNBUTTON: - X: PARENT_RIGHT - WIDTH - Y: 150 - Width: 140 - Height: 25 - Font: Regular DropDownButton@TECHLEVEL_DROPDOWNBUTTON: X: 85 Y: 150 @@ -197,6 +197,19 @@ Background@LOBBY_OPTIONS_BIN: Height: 25 Text: Tech Level: Align: Right + Label@GAMESPEED_DESC: + X: PARENT_RIGHT - WIDTH - 145 + Y: 150 + Width: 120 + Height: 25 + Text: Game Speed: + Align: Right + DropDownButton@GAMESPEED_DROPDOWNBUTTON: + X: PARENT_RIGHT - WIDTH + Y: 150 + Width: 140 + Height: 25 + Font: Regular Background@FORCE_START_DIALOG: X: 20 diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index 203c5c5013..2cded39907 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -213,3 +213,21 @@ SpriteSequenceFormat: TilesetSpecificSpriteSequence SNOW: .sno INTERIOR: .int DESERT: .des + +GameSpeeds: + slower: + Name: Slower + Timestep: 50 + OrderLatency: 3 + default: + Name: Default + Timestep: 40 + OrderLatency: 3 + faster: + Name: Faster + Timestep: 30 + OrderLatency: 4 + fastest: + Name: Fastest + Timestep: 20 + OrderLatency: 6 diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 92f5b64869..842f0719c5 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -258,3 +258,21 @@ SpriteSequenceFormat: TilesetSpecificSpriteSequence TilesetCodes: TEMPERATE: t SNOW: a + +GameSpeeds: + slower: + Name: Slower + Timestep: 50 + OrderLatency: 3 + default: + Name: Default + Timestep: 40 + OrderLatency: 3 + faster: + Name: Faster + Timestep: 30 + OrderLatency: 4 + fastest: + Name: Fastest + Timestep: 20 + OrderLatency: 6 From 1109ec53d1fe6c73a2185a5040a75e5e0605dc31 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 3 Sep 2015 22:23:37 +0100 Subject: [PATCH 3/3] Update UI timers for variable game speed. --- OpenRA.Game/Widgets/WidgetUtils.cs | 8 +++---- .../Scripting/Global/UtilsGlobal.cs | 9 ++++++-- .../Widgets/Logic/Ingame/GameTimerLogic.cs | 2 +- .../Logic/Ingame/ProductionTooltipLogic.cs | 4 ++-- .../Logic/Ingame/ReplayControlBarLogic.cs | 22 ++++++++++--------- .../Logic/Ingame/SupportPowerTooltipLogic.cs | 7 +++--- .../Widgets/ObserverProductionIconsWidget.cs | 6 ++--- .../ObserverSupportPowerIconsWidget.cs | 6 ++--- .../Widgets/ProductionPaletteWidget.cs | 8 +++---- .../Widgets/StrategicProgressWidget.cs | 2 +- .../Widgets/SupportPowerTimerWidget.cs | 4 +++- .../Widgets/SupportPowersWidget.cs | 6 ++--- 12 files changed, 47 insertions(+), 37 deletions(-) diff --git a/OpenRA.Game/Widgets/WidgetUtils.cs b/OpenRA.Game/Widgets/WidgetUtils.cs index fc093a1f61..ac8c1ac7b2 100644 --- a/OpenRA.Game/Widgets/WidgetUtils.cs +++ b/OpenRA.Game/Widgets/WidgetUtils.cs @@ -158,14 +158,14 @@ namespace OpenRA.Widgets DrawRGBA(cornerBottomRight, new float2(bounds.Right - cornerBottomRight.Size.X, bounds.Bottom - cornerBottomRight.Size.Y)); } - public static string FormatTime(int ticks) + public static string FormatTime(int ticks, int timestep) { - return FormatTime(ticks, true); + return FormatTime(ticks, true, timestep); } - public static string FormatTime(int ticks, bool leadingMinuteZero) + public static string FormatTime(int ticks, bool leadingMinuteZero, int timestep) { - var seconds = (int)Math.Ceiling(ticks / 25f); + var seconds = (int)Math.Ceiling(ticks * timestep / 1000f); return FormatTimeSeconds(seconds, leadingMinuteZero); } diff --git a/OpenRA.Mods.Common/Scripting/Global/UtilsGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/UtilsGlobal.cs index 08acf4219e..c5aba7459b 100644 --- a/OpenRA.Mods.Common/Scripting/Global/UtilsGlobal.cs +++ b/OpenRA.Mods.Common/Scripting/Global/UtilsGlobal.cs @@ -19,7 +19,12 @@ namespace OpenRA.Mods.Common.Scripting [ScriptGlobal("Utils")] public class UtilsGlobal : ScriptGlobal { - public UtilsGlobal(ScriptContext context) : base(context) { } + readonly World world; + public UtilsGlobal(ScriptContext context) + : base(context) + { + world = context.World; + } [Desc("Calls a function on every element in a collection.")] public void Do(LuaValue[] collection, LuaFunction func) @@ -101,7 +106,7 @@ namespace OpenRA.Mods.Common.Scripting [Desc("Returns the ticks formatted to HH:MM:SS.")] public string FormatTime(int ticks, bool leadingMinuteZero = true) { - return WidgetUtils.FormatTime(ticks, leadingMinuteZero); + return WidgetUtils.FormatTime(ticks, leadingMinuteZero, world.Timestep); } } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs index 4de3ed228b..bf2c8d8564 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs @@ -44,7 +44,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (status == null && shouldShowStatus()) return statusText(); - return WidgetUtils.FormatTime(world.WorldTick); + return WidgetUtils.FormatTime(world.WorldTick, world.Timestep); }; } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs index 5ddcfd5733..13f05dceaa 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs @@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic public class ProductionTooltipLogic { [ObjectCreator.UseCtor] - public ProductionTooltipLogic(Widget widget, TooltipContainerWidget tooltipContainer, ProductionPaletteWidget palette) + public ProductionTooltipLogic(Widget widget, TooltipContainerWidget tooltipContainer, ProductionPaletteWidget palette, World world) { var mapRules = palette.World.Map.Rules; var pm = palette.World.LocalPlayer.PlayerActor.Trait(); @@ -83,7 +83,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var lowpower = pm.PowerState != PowerState.Normal; var time = palette.CurrentQueue == null ? 0 : palette.CurrentQueue.GetBuildTime(actor.Name) * (lowpower ? palette.CurrentQueue.Info.LowPowerSlowdown : 1); - var timeString = WidgetUtils.FormatTime(time); + var timeString = WidgetUtils.FormatTime(time, world.Timestep); timeLabel.GetText = () => timeString; timeLabel.GetColor = () => lowpower ? Color.Red : Color.White; diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ReplayControlBarLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ReplayControlBarLogic.cs index acd3a781cd..be92013d90 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ReplayControlBarLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ReplayControlBarLogic.cs @@ -8,6 +8,7 @@ */ #endregion +using System; using System.Collections.Generic; using OpenRA.Network; using OpenRA.Widgets; @@ -18,12 +19,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic { enum PlaybackSpeed { Regular, Slow, Fast, Maximum } - readonly Dictionary timesteps = new Dictionary() + readonly Dictionary multipliers = new Dictionary() { - { PlaybackSpeed.Regular, Game.Timestep }, - { PlaybackSpeed.Slow, Game.Timestep * 2 }, - { PlaybackSpeed.Fast, Game.Timestep / 2 }, - { PlaybackSpeed.Maximum, 1 }, + { PlaybackSpeed.Regular, 1 }, + { PlaybackSpeed.Slow, 2 }, + { PlaybackSpeed.Fast, 0.5f }, + { PlaybackSpeed.Maximum, 0.001f }, }; [ObjectCreator.UseCtor] @@ -41,6 +42,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic container.Visible = true; var speed = PlaybackSpeed.Regular; + var originalTimestep = world.Timestep; var pauseButton = widget.Get("BUTTON_PAUSE"); pauseButton.IsVisible = () => world.Timestep != 0 && orderManager.NetFrameNumber < replayNetTicks; @@ -48,7 +50,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var playButton = widget.Get("BUTTON_PLAY"); playButton.IsVisible = () => world.Timestep == 0 || orderManager.NetFrameNumber >= replayNetTicks; - playButton.OnClick = () => world.Timestep = timesteps[speed]; + playButton.OnClick = () => world.Timestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); playButton.IsDisabled = () => orderManager.NetFrameNumber >= replayNetTicks; var slowButton = widget.Get("BUTTON_SLOW"); @@ -58,7 +60,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { speed = PlaybackSpeed.Slow; if (world.Timestep != 0) - world.Timestep = timesteps[speed]; + world.Timestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); }; var normalSpeedButton = widget.Get("BUTTON_REGULAR"); @@ -68,7 +70,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { speed = PlaybackSpeed.Regular; if (world.Timestep != 0) - world.Timestep = timesteps[speed]; + world.Timestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); }; var fastButton = widget.Get("BUTTON_FAST"); @@ -78,7 +80,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { speed = PlaybackSpeed.Fast; if (world.Timestep != 0) - world.Timestep = timesteps[speed]; + world.Timestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); }; var maximumButton = widget.Get("BUTTON_MAXIMUM"); @@ -88,7 +90,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { speed = PlaybackSpeed.Maximum; if (world.Timestep != 0) - world.Timestep = timesteps[speed]; + world.Timestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); }; } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/SupportPowerTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/SupportPowerTooltipLogic.cs index f55aae2244..28de306f92 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/SupportPowerTooltipLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/SupportPowerTooltipLogic.cs @@ -17,7 +17,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic public class SupportPowerTooltipLogic { [ObjectCreator.UseCtor] - public SupportPowerTooltipLogic(Widget widget, TooltipContainerWidget tooltipContainer, SupportPowersWidget palette) + public SupportPowerTooltipLogic(Widget widget, TooltipContainerWidget tooltipContainer, SupportPowersWidget palette, World world) { widget.IsVisible = () => palette.TooltipIcon != null; var nameLabel = widget.Get("NAME"); @@ -46,8 +46,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (sp.Info == null) return; // no instances actually exist (race with destroy) - time = "{0} / {1}".F(WidgetUtils.FormatTime(sp.RemainingTime), - WidgetUtils.FormatTime(sp.Info.ChargeTime * 25)); + var remaining = WidgetUtils.FormatTime(sp.RemainingTime, world.Timestep); + var total = WidgetUtils.FormatTime(sp.Info.ChargeTime * 25, world.Timestep); + time = "{0} / {1}".F(remaining, total); if (sp == lastPower) return; diff --git a/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs b/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs index d44c9ab2bf..8acaa27032 100644 --- a/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs @@ -92,18 +92,18 @@ namespace OpenRA.Mods.Common.Widgets WidgetUtils.DrawSHPCentered(clock.Image, location + 0.5f * iconSize, worldRenderer.Palette(bi.IconPalette), 0.5f); var tiny = Game.Renderer.Fonts["Tiny"]; - var text = GetOverlayForItem(current); + var text = GetOverlayForItem(current, world.Timestep); tiny.DrawTextWithContrast(text, location + new float2(16, 16) - new float2(tiny.Measure(text).X / 2, 0), Color.White, Color.Black, 1); } } - static string GetOverlayForItem(ProductionItem item) + static string GetOverlayForItem(ProductionItem item, int timestep) { if (item.Paused) return "ON HOLD"; if (item.Done) return "READY"; - return WidgetUtils.FormatTime(item.RemainingTimeActual); + return WidgetUtils.FormatTime(item.RemainingTimeActual, timestep); } public override Widget Clone() diff --git a/OpenRA.Mods.Common/Widgets/ObserverSupportPowerIconsWidget.cs b/OpenRA.Mods.Common/Widgets/ObserverSupportPowerIconsWidget.cs index 7541c3cf64..c167828722 100644 --- a/OpenRA.Mods.Common/Widgets/ObserverSupportPowerIconsWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ObserverSupportPowerIconsWidget.cs @@ -82,18 +82,18 @@ namespace OpenRA.Mods.Common.Widgets WidgetUtils.DrawSHPCentered(clock.Image, location + 0.5f * iconSize, worldRenderer.Palette(item.Info.IconPalette), 0.5f); var tiny = Game.Renderer.Fonts["Tiny"]; - var text = GetOverlayForItem(item); + var text = GetOverlayForItem(item, world.Timestep); tiny.DrawTextWithContrast(text, location + new float2(16, 16) - new float2(tiny.Measure(text).X / 2, 0), Color.White, Color.Black, 1); } } - static string GetOverlayForItem(SupportPowerInstance item) + static string GetOverlayForItem(SupportPowerInstance item, int timestep) { if (item.Disabled) return "ON HOLD"; if (item.Ready) return "READY"; - return WidgetUtils.FormatTime(item.RemainingTime); + return WidgetUtils.FormatTime(item.RemainingTime, timestep); } public override Widget Clone() diff --git a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs index a3ca17598d..5fad243947 100644 --- a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs @@ -162,7 +162,7 @@ namespace OpenRA.Mods.Common.Widgets { if (TooltipContainer != null) tooltipContainer.Value.SetTooltip(TooltipTemplate, - new WidgetArgs() { { "palette", this } }); + new WidgetArgs() { { "world", World }, { "palette", this } }); } public override void MouseExited() @@ -353,7 +353,7 @@ namespace OpenRA.Mods.Common.Widgets var iconOffset = 0.5f * IconSize.ToFloat2() + IconSpriteOffset; overlayFont = Game.Renderer.Fonts["TinyBold"]; - timeOffset = iconOffset - overlayFont.Measure(WidgetUtils.FormatTime(0)) / 2; + timeOffset = iconOffset - overlayFont.Measure(WidgetUtils.FormatTime(0, World.Timestep)) / 2; queuedOffset = new float2(4, 2); holdOffset = iconOffset - overlayFont.Measure(HoldText) / 2; readyOffset = iconOffset - overlayFont.Measure(ReadyText) / 2; @@ -400,7 +400,7 @@ namespace OpenRA.Mods.Common.Widgets var waiting = first != CurrentQueue.CurrentItem() && !first.Done; if (first.Done) { - if (ReadyTextStyle == ReadyTextStyleOptions.Solid || orderManager.LocalFrameNumber / 9 % 2 == 0) + if (ReadyTextStyle == ReadyTextStyleOptions.Solid || orderManager.LocalFrameNumber * worldRenderer.World.Timestep / 360 % 2 == 0) overlayFont.DrawTextWithContrast(ReadyText, icon.Pos + readyOffset, Color.White, Color.Black, 1); else if (ReadyTextStyle == ReadyTextStyleOptions.AlternatingColor) overlayFont.DrawTextWithContrast(ReadyText, icon.Pos + readyOffset, ReadyTextAltColor, Color.Black, 1); @@ -410,7 +410,7 @@ namespace OpenRA.Mods.Common.Widgets icon.Pos + holdOffset, Color.White, Color.Black, 1); else if (!waiting) - overlayFont.DrawTextWithContrast(WidgetUtils.FormatTime(first.RemainingTimeActual), + overlayFont.DrawTextWithContrast(WidgetUtils.FormatTime(first.RemainingTimeActual, World.Timestep), icon.Pos + timeOffset, Color.White, Color.Black, 1); diff --git a/OpenRA.Mods.Common/Widgets/StrategicProgressWidget.cs b/OpenRA.Mods.Common/Widgets/StrategicProgressWidget.cs index ef46e7dd28..2f60452728 100644 --- a/OpenRA.Mods.Common/Widgets/StrategicProgressWidget.cs +++ b/OpenRA.Mods.Common/Widgets/StrategicProgressWidget.cs @@ -65,7 +65,7 @@ namespace OpenRA.Mods.Common.Widgets var isVictory = pendingWinner == world.LocalPlayer || !WorldUtils.AreMutualAllies(pendingWinner, world.LocalPlayer); var tc = "Strategic {0} in {1}".F( isVictory ? "victory" : "defeat", - WidgetUtils.FormatTime(winnerSvc.TicksLeft)); + WidgetUtils.FormatTime(winnerSvc.TicksLeft, world.Timestep)); var font = Game.Renderer.Fonts["Bold"]; diff --git a/OpenRA.Mods.Common/Widgets/SupportPowerTimerWidget.cs b/OpenRA.Mods.Common/Widgets/SupportPowerTimerWidget.cs index e53fb129d1..6ea4c8ef1b 100644 --- a/OpenRA.Mods.Common/Widgets/SupportPowerTimerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/SupportPowerTimerWidget.cs @@ -23,12 +23,14 @@ namespace OpenRA.Mods.Common.Widgets public readonly string Format = "{0}: {1}"; public readonly TimerOrder Order = TimerOrder.Descending; + readonly World world; readonly IEnumerable powers; Pair[] texts; [ObjectCreator.UseCtor] public SupportPowerTimerWidget(World world) { + this.world = world; powers = world.ActorsWithTrait() .Where(p => !p.Actor.IsDead && !p.Actor.Owner.NonCombatant) .SelectMany(s => s.Trait.Powers.Values) @@ -39,7 +41,7 @@ namespace OpenRA.Mods.Common.Widgets { texts = powers.Select(p => { - var time = WidgetUtils.FormatTime(p.RemainingTime, false); + var time = WidgetUtils.FormatTime(p.RemainingTime, false, world.Timestep); var text = Format.F(p.Info.Description, time); var color = !p.Ready || Game.LocalTick % 50 < 25 ? p.Instances[0].Self.Owner.Color.RGB : Color.White; return Pair.New(text, color); diff --git a/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs b/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs index bbbe88f62b..6c1a4accaa 100644 --- a/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs +++ b/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs @@ -142,7 +142,7 @@ namespace OpenRA.Mods.Common.Widgets holdOffset = iconOffset - overlayFont.Measure(HoldText) / 2; readyOffset = iconOffset - overlayFont.Measure(ReadyText) / 2; - timeOffset = iconOffset - overlayFont.Measure(WidgetUtils.FormatTime(0)) / 2; + timeOffset = iconOffset - overlayFont.Measure(WidgetUtils.FormatTime(0, worldRenderer.World.Timestep)) / 2; // Icons foreach (var p in icons.Values) @@ -171,7 +171,7 @@ namespace OpenRA.Mods.Common.Widgets p.Pos + holdOffset, Color.White, Color.Black, 1); else - overlayFont.DrawTextWithContrast(WidgetUtils.FormatTime(p.Power.RemainingTime), + overlayFont.DrawTextWithContrast(WidgetUtils.FormatTime(p.Power.RemainingTime, worldRenderer.World.Timestep), p.Pos + timeOffset, Color.White, Color.Black, 1); } @@ -189,7 +189,7 @@ namespace OpenRA.Mods.Common.Widgets return; tooltipContainer.Value.SetTooltip(TooltipTemplate, - new WidgetArgs() { { "palette", this } }); + new WidgetArgs() { { "world", worldRenderer.World }, { "palette", this } }); } public override void MouseExited()