From 26d712728a06c70aa98912ed53bfaf49e25b9c0a Mon Sep 17 00:00:00 2001 From: Oliver Brakmann Date: Sat, 26 Jan 2019 19:28:52 +0100 Subject: [PATCH] Add scripting support for TimeLimitManager --- .../Scripting/Global/DateTimeGlobal.cs | 43 ++++++++++++++++++- .../Scripting/Global/TriggerGlobal.cs | 6 +++ .../Scripting/ScriptTriggers.cs | 23 +++++++++- .../Traits/Player/TimeLimitManager.cs | 34 ++++++++++++++- 4 files changed, 101 insertions(+), 5 deletions(-) diff --git a/OpenRA.Mods.Common/Scripting/Global/DateTimeGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/DateTimeGlobal.cs index 8375350f39..bb9a0aba73 100644 --- a/OpenRA.Mods.Common/Scripting/Global/DateTimeGlobal.cs +++ b/OpenRA.Mods.Common/Scripting/Global/DateTimeGlobal.cs @@ -10,6 +10,8 @@ #endregion using System; +using Eluant; +using OpenRA.Mods.Common.Traits; using OpenRA.Scripting; namespace OpenRA.Mods.Common.Scripting @@ -17,8 +19,13 @@ namespace OpenRA.Mods.Common.Scripting [ScriptGlobal("DateTime")] public class DateGlobal : ScriptGlobal { + readonly TimeLimitManager tlm; + public DateGlobal(ScriptContext context) - : base(context) { } + : base(context) + { + tlm = context.World.WorldActor.TraitOrDefault(); + } [Desc("True on the 31st of October.")] public bool IsHalloween @@ -43,5 +50,39 @@ namespace OpenRA.Mods.Common.Scripting { return Seconds(minutes * 60); } + + [Desc("Return or set the time limit (in ticks). When setting, the time limit will count from now. Setting the time limit to 0 will disable it.")] + public int TimeLimit + { + get + { + return tlm != null ? tlm.TimeLimit : 0; + } + + set + { + if (tlm != null) + tlm.TimeLimit = value == 0 ? 0 : value + GameTime; + else + throw new LuaException("Cannot set TimeLimit, TimeLimitManager trait is missing."); + } + } + + [Desc("The notification string used for custom time limit warnings. See the TimeLimitManager trait documentation for details.")] + public string TimeLimitNotification + { + get + { + return tlm != null ? tlm.Notification : null; + } + + set + { + if (tlm != null) + tlm.Notification = value; + else + throw new LuaException("Cannot set TimeLimitNotification, TimeLimitManager trait is missing."); + } + } } } diff --git a/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs index 6c631624c6..6cf889d8aa 100644 --- a/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs +++ b/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs @@ -470,6 +470,12 @@ namespace OpenRA.Mods.Common.Scripting GetScriptTriggers(a).RegisterCallback(Trigger.OnSold, func, Context); } + [Desc("Call a function when the game timer expires. The callback function will be called as func().")] + public void OnTimerExpired(LuaFunction func) + { + GetScriptTriggers(Context.World.WorldActor).RegisterCallback(Trigger.OnTimerExpired, func, Context); + } + [Desc("Removes all triggers from this actor. " + "Note that the removal will only take effect at the end of a tick, " + "so you must not add new triggers at the same time that you are calling this function.")] diff --git a/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs b/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs index 2fd024ef5b..6c77e895a1 100644 --- a/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs +++ b/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs @@ -24,7 +24,7 @@ namespace OpenRA.Mods.Common.Scripting OnIdle, OnDamaged, OnKilled, OnProduction, OnOtherProduction, OnPlayerWon, OnPlayerLost, OnObjectiveAdded, OnObjectiveCompleted, OnObjectiveFailed, OnCapture, OnInfiltrated, OnAddedToWorld, OnRemovedFromWorld, OnDiscovered, OnPlayerDiscovered, - OnPassengerEntered, OnPassengerExited, OnSold + OnPassengerEntered, OnPassengerExited, OnSold, OnTimerExpired } [Desc("Allows map scripts to attach triggers to this actor via the Triggers global.")] @@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Scripting public sealed class ScriptTriggers : INotifyIdle, INotifyDamage, INotifyKilled, INotifyProduction, INotifyOtherProduction, INotifyObjectivesUpdated, INotifyCapture, INotifyInfiltrated, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyDiscovered, INotifyActorDisposing, - INotifyPassengerEntered, INotifyPassengerExited, INotifySold, INotifyWinStateChanged + INotifyPassengerEntered, INotifyPassengerExited, INotifySold, INotifyWinStateChanged, INotifyTimeLimit { readonly World world; readonly Actor self; @@ -493,6 +493,25 @@ namespace OpenRA.Mods.Common.Scripting } } + void INotifyTimeLimit.NotifyTimerExpired(Actor self) + { + if (world.Disposing) + return; + + foreach (var f in Triggerables(Trigger.OnTimerExpired)) + { + try + { + f.Function.Call().Dispose(); + } + catch (Exception ex) + { + f.Context.FatalError(ex.Message); + return; + } + } + } + public void Clear(Trigger trigger) { world.AddFrameEndTask(w => diff --git a/OpenRA.Mods.Common/Traits/Player/TimeLimitManager.cs b/OpenRA.Mods.Common/Traits/Player/TimeLimitManager.cs index 9a637cc898..3b6b95cd6f 100644 --- a/OpenRA.Mods.Common/Traits/Player/TimeLimitManager.cs +++ b/OpenRA.Mods.Common/Traits/Player/TimeLimitManager.cs @@ -12,8 +12,10 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.Mods.Common.Widgets; using OpenRA.Primitives; using OpenRA.Traits; +using OpenRA.Widgets; namespace OpenRA.Mods.Common.Traits { @@ -55,6 +57,18 @@ namespace OpenRA.Mods.Common.Traits [Desc("Notification text for time limit warnings. The string '{0}' will be replaced by the remaining time in minutes, '{1}' is used for the plural form.")] public readonly string Notification = "{0} minute{1} remaining."; + [Desc("ID of the LabelWidget used to display a text ingame that will be updated every second.")] + public readonly string CountdownLabel = null; + + [Desc("Text to be shown using the CountdownLabel. The string '{0}' will be replaced by the time in hh:mm:ss format.")] + public readonly string CountdownText = null; + + [Desc("Will prevent showing/playing the built-in time limit warnings when set to true.")] + public readonly bool SkipTimeRemainingNotifications = false; + + [Desc("Will prevent showing/playing the built-in timer expired notification when set to true.")] + public readonly bool SkipTimerExpiredNotification = false; + IEnumerable ILobbyOptions.LobbyOptions(Ruleset rules) { var timelimits = TimeLimitOptions.ToDictionary(c => c.ToString(), c => @@ -76,6 +90,8 @@ namespace OpenRA.Mods.Common.Traits { readonly TimeLimitManagerInfo info; MapOptions mapOptions; + LabelWidget countdownLabel; + CachedTransform countdown; int ticksRemaining; public int TimeLimit; @@ -97,6 +113,16 @@ namespace OpenRA.Mods.Common.Traits void IWorldLoaded.WorldLoaded(World w, OpenRA.Graphics.WorldRenderer wr) { mapOptions = w.WorldActor.Trait(); + if (string.IsNullOrWhiteSpace(info.CountdownLabel) || string.IsNullOrWhiteSpace(info.CountdownText)) + return; + + countdownLabel = Ui.Root.GetOrNull(info.CountdownLabel); + if (countdownLabel != null) + { + countdown = new CachedTransform(t => + info.CountdownText.F(WidgetUtils.FormatTime(t, true, w.IsReplay ? mapOptions.GameSpeed.Timestep : w.Timestep))); + countdownLabel.GetText = () => countdown.Update(ticksRemaining); + } } void ITick.Tick(Actor self) @@ -119,7 +145,7 @@ namespace OpenRA.Mods.Common.Traits return; } - if (ticksRemaining < 0) + if (ticksRemaining < 0 || info.SkipTimeRemainingNotifications) return; foreach (var m in info.TimeLimitWarnings.Keys) @@ -136,7 +162,11 @@ namespace OpenRA.Mods.Common.Traits void INotifyTimeLimit.NotifyTimerExpired(Actor self) { - Game.AddSystemLine("Battlefield Control", "Time limit has expired."); + if (countdownLabel != null) + countdownLabel.GetText = () => null; + + if (!info.SkipTimerExpiredNotification) + Game.AddSystemLine("Battlefield Control", "Time limit has expired."); } } }