summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/release-notes.md1
-rw-r--r--src/SMAPI/Events/IGameLoopEvents.cs6
-rw-r--r--src/SMAPI/Events/OneSecondUpdateTickedEventArgs.cs32
-rw-r--r--src/SMAPI/Events/OneSecondUpdateTickingEventArgs.cs32
-rw-r--r--src/SMAPI/Framework/Events/EventManager.cs8
-rw-r--r--src/SMAPI/Framework/Events/ModGameLoopEvents.cs14
-rw-r--r--src/SMAPI/Framework/SGame.cs5
-rw-r--r--src/SMAPI/StardewModdingAPI.csproj2
8 files changed, 100 insertions, 0 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md
index 15747488..5293f1c1 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -5,6 +5,7 @@
* Tweaked installer to reduce antivirus false positives.
* For modders:
+ * Added `GameLoop.OneSecondUpdateTicking/Ticked` events.
* Added `Specialised.LoadStageChanged` for mods which need to do something at a specific point in the game's save loading process.
* You can now use read/write save data as soon as the save is loaded (instead of once the world is initialised).
diff --git a/src/SMAPI/Events/IGameLoopEvents.cs b/src/SMAPI/Events/IGameLoopEvents.cs
index ea79aa74..6fb56c8b 100644
--- a/src/SMAPI/Events/IGameLoopEvents.cs
+++ b/src/SMAPI/Events/IGameLoopEvents.cs
@@ -14,6 +14,12 @@ namespace StardewModdingAPI.Events
/// <summary>Raised after the game state is updated (≈60 times per second).</summary>
event EventHandler<UpdateTickedEventArgs> UpdateTicked;
+ /// <summary>Raised once per second before the game state is updated.</summary>
+ event EventHandler<OneSecondUpdateTickingEventArgs> OneSecondUpdateTicking;
+
+ /// <summary>Raised once per second after the game state is updated.</summary>
+ event EventHandler<OneSecondUpdateTickedEventArgs> OneSecondUpdateTicked;
+
/// <summary>Raised before the game creates a new save file.</summary>
event EventHandler<SaveCreatingEventArgs> SaveCreating;
diff --git a/src/SMAPI/Events/OneSecondUpdateTickedEventArgs.cs b/src/SMAPI/Events/OneSecondUpdateTickedEventArgs.cs
new file mode 100644
index 00000000..d330502a
--- /dev/null
+++ b/src/SMAPI/Events/OneSecondUpdateTickedEventArgs.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace StardewModdingAPI.Events
+{
+ /// <summary>Event arguments for an <see cref="IGameLoopEvents.OneSecondUpdateTicked"/> event.</summary>
+ public class OneSecondUpdateTickedEventArgs : EventArgs
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The number of ticks elapsed since the game started, including the current tick.</summary>
+ public uint Ticks { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="ticks">The number of ticks elapsed since the game started, including the current tick.</param>
+ internal OneSecondUpdateTickedEventArgs(uint ticks)
+ {
+ this.Ticks = ticks;
+ }
+
+ /// <summary>Get whether <see cref="Ticks"/> is a multiple of the given <paramref name="number"/>. This is mainly useful if you want to run logic intermittently (e.g. <code>e.IsMultipleOf(30)</code> for every half-second).</summary>
+ /// <param name="number">The factor to check.</param>
+ public bool IsMultipleOf(uint number)
+ {
+ return this.Ticks % number == 0;
+ }
+ }
+}
diff --git a/src/SMAPI/Events/OneSecondUpdateTickingEventArgs.cs b/src/SMAPI/Events/OneSecondUpdateTickingEventArgs.cs
new file mode 100644
index 00000000..cdd9f4cc
--- /dev/null
+++ b/src/SMAPI/Events/OneSecondUpdateTickingEventArgs.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace StardewModdingAPI.Events
+{
+ /// <summary>Event arguments for an <see cref="IGameLoopEvents.OneSecondUpdateTicking"/> event.</summary>
+ public class OneSecondUpdateTickingEventArgs : EventArgs
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The number of ticks elapsed since the game started, including the current tick.</summary>
+ public uint Ticks { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="ticks">The number of ticks elapsed since the game started, including the current tick.</param>
+ internal OneSecondUpdateTickingEventArgs(uint ticks)
+ {
+ this.Ticks = ticks;
+ }
+
+ /// <summary>Get whether <see cref="Ticks"/> is a multiple of the given <paramref name="number"/>. This is mainly useful if you want to run logic intermittently (e.g. <code>e.IsMultipleOf(30)</code> for every half-second).</summary>
+ /// <param name="number">The factor to check.</param>
+ public bool IsMultipleOf(uint number)
+ {
+ return this.Ticks % number == 0;
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs
index b7f00f52..13244601 100644
--- a/src/SMAPI/Framework/Events/EventManager.cs
+++ b/src/SMAPI/Framework/Events/EventManager.cs
@@ -58,6 +58,12 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>Raised after the game performs its overall update tick (≈60 times per second).</summary>
public readonly ManagedEvent<UpdateTickedEventArgs> UpdateTicked;
+ /// <summary>Raised once per second before the game performs its overall update tick.</summary>
+ public readonly ManagedEvent<OneSecondUpdateTickingEventArgs> OneSecondUpdateTicking;
+
+ /// <summary>Raised once per second after the game performs its overall update tick.</summary>
+ public readonly ManagedEvent<OneSecondUpdateTickedEventArgs> OneSecondUpdateTicked;
+
/// <summary>Raised before the game creates the save file.</summary>
public readonly ManagedEvent<SaveCreatingEventArgs> SaveCreating;
@@ -380,6 +386,8 @@ namespace StardewModdingAPI.Framework.Events
this.GameLaunched = ManageEventOf<GameLaunchedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.GameLaunched));
this.UpdateTicking = ManageEventOf<UpdateTickingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.UpdateTicking));
this.UpdateTicked = ManageEventOf<UpdateTickedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.UpdateTicked));
+ this.OneSecondUpdateTicking = ManageEventOf<OneSecondUpdateTickingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.OneSecondUpdateTicking));
+ this.OneSecondUpdateTicked = ManageEventOf<OneSecondUpdateTickedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.OneSecondUpdateTicked));
this.SaveCreating = ManageEventOf<SaveCreatingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.SaveCreating));
this.SaveCreated = ManageEventOf<SaveCreatedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.SaveCreated));
this.Saving = ManageEventOf<SavingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.Saving));
diff --git a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs
index 3a764ab0..0177c22e 100644
--- a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs
+++ b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs
@@ -30,6 +30,20 @@ namespace StardewModdingAPI.Framework.Events
remove => this.EventManager.UpdateTicked.Remove(value);
}
+ /// <summary>Raised once per second before the game state is updated.</summary>
+ public event EventHandler<OneSecondUpdateTickingEventArgs> OneSecondUpdateTicking
+ {
+ add => this.EventManager.OneSecondUpdateTicking.Add(value);
+ remove => this.EventManager.OneSecondUpdateTicking.Remove(value);
+ }
+
+ /// <summary>Raised once per second after the game state is updated.</summary>
+ public event EventHandler<OneSecondUpdateTickedEventArgs> OneSecondUpdateTicked
+ {
+ add => this.EventManager.OneSecondUpdateTicked.Add(value);
+ remove => this.EventManager.OneSecondUpdateTicked.Remove(value);
+ }
+
/// <summary>Raised before the game creates a new save file.</summary>
public event EventHandler<SaveCreatingEventArgs> SaveCreating
{
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index cb62de2a..8abe4d16 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -878,8 +878,11 @@ namespace StardewModdingAPI.Framework
this.OnLoadStageChanged(LoadStage.Loaded);
// update tick
+ bool isOneSecond = this.TicksElapsed % 60 == 0;
this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed));
this.Events.UpdateTicking.Raise(new UpdateTickingEventArgs(this.TicksElapsed));
+ if (isOneSecond)
+ this.Events.OneSecondUpdateTicking.Raise(new OneSecondUpdateTickingEventArgs(this.TicksElapsed));
try
{
this.Input.UpdateSuppression();
@@ -891,6 +894,8 @@ namespace StardewModdingAPI.Framework
}
this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed));
this.Events.UpdateTicked.Raise(new UpdateTickedEventArgs(this.TicksElapsed));
+ if (isOneSecond)
+ this.Events.OneSecondUpdateTicked.Raise(new OneSecondUpdateTickedEventArgs(this.TicksElapsed));
/*********
** Update events
diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj
index fdb0c6c7..5540f277 100644
--- a/src/SMAPI/StardewModdingAPI.csproj
+++ b/src/SMAPI/StardewModdingAPI.csproj
@@ -135,6 +135,8 @@
<Compile Include="Events\MultiplayerEvents.cs" />
<Compile Include="Events\NpcListChangedEventArgs.cs" />
<Compile Include="Events\ObjectListChangedEventArgs.cs" />
+ <Compile Include="Events\OneSecondUpdateTickedEventArgs.cs" />
+ <Compile Include="Events\OneSecondUpdateTickingEventArgs.cs" />
<Compile Include="Events\PeerContextReceivedEventArgs.cs" />
<Compile Include="Events\PeerDisconnectedEventArgs.cs" />
<Compile Include="Events\PlayerEvents.cs" />