Difference between revisions of "Modding:Modder Guide/APIs/Events"

From Stardew Valley Wiki
Jump to navigation Jump to search
(→‎Player: + e.IsLocalPlayer)
(remove {{SMAPI upcoming|4.0.0}})
 
(53 intermediate revisions by 7 users not shown)
Line 3: Line 3:
 
SMAPI provides several C# events which let your mod respond when something happens (like when the player places an object) or run code periodically (like once per update tick).
 
SMAPI provides several C# events which let your mod respond when something happens (like when the player places an object) or run code periodically (like once per update tick).
  
==Intro==
+
==FAQs==
A [https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/events/ C# event] is the way that SMAPI notifies mods when something happens. You can add any number of ''event handlers'' (methods to call) when an event is raised. Event handlers are usually added in your <tt>Entry</tt> method, though you can add and remove them anytime.
+
===What are events?===
 +
Events let you run code when something happens. You can add any number of ''event handlers'' (methods to call) when an ''event'' (what happened) is raised. You can think of events and handlers as a ''when..then'' statement:
 +
<pre>
 +
WHEN the save is loaded,  <-- event
 +
THEN run my code          <-- event handler
 +
</pre>
  
For example, let's say you want to print a message when the player loads a save. First you would choose the appropriate event from the list below (<tt>SaveEvents.AfterLoad</tt>), then add an event handler, and do something in your method code:
+
See [https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/events/ ''Events'' in the C# programming guide] for more info.
<source lang="c#">
+
 
 +
===How do I use them?===
 +
Event handlers are usually added in your <samp>Entry</samp> method, though you can add and remove them anytime. For example, let's say you want to print a message when each day starts. First you would choose the appropriate event from the list below (<samp>[[#GameLoop.DayStarted|GameLoop.DayStarted]]</samp>), then add an event handler, and do something in your method code:
 +
<syntaxhighlight lang="c#">
 
/// <summary>The main entry point for the mod.</summary>
 
/// <summary>The main entry point for the mod.</summary>
public class ModEntry : Mod
+
internal sealed class ModEntry : Mod
 
{
 
{
 
     /**********
 
     /**********
Line 18: Line 26:
 
     public override void Entry(IModHelper helper)
 
     public override void Entry(IModHelper helper)
 
     {
 
     {
         // SaveEvents.AfterLoad is the event
+
         // event += method to call
        // this.SaveEvents_AfterLoad is the method below you want to call
+
         helper.Events.GameLoop.DayStarted += this.OnDayStarted;
         SaveEvents.AfterLoad += this.SaveEvents_AfterLoad;
 
 
     }
 
     }
  
     /// <summary>The method called after the player loads their save.</summary>
+
     /// <summary>The method called after a new day starts.</summary>
 
     /// <param name="sender">The event sender.</param>
 
     /// <param name="sender">The event sender.</param>
 
     /// <param name="e">The event arguments.</param>
 
     /// <param name="e">The event arguments.</param>
     private void SaveEvents_AfterLoad(object sender, EventArgs e)
+
     private void OnDayStarted(object? sender, DayStartedEventArgs e)
 
     {
 
     {
       this.Monitor.Log("The player loaded their game! This is a good time to do things.");
+
       this.Monitor.Log("A new day dawns!", LogLevel.Info);
      this.Monitor.Log("Everything in the world is ready to interact with at this point.");
 
 
     }
 
     }
 
}
 
}
</source>
+
</syntaxhighlight>
  
Tip: you don't need to memorise the method arguments. In Visual Studio, type <code>SaveEvents.AfterLoad +=</code> and press {{key|TAB}} to auto-create a method. (There doesn't seem to be an equivalent feature in MonoDevelop.)
+
Tip: you don't need to memorise the method arguments. In Visual Studio, type <code>helper.Events.GameLoop.SaveLoaded +=</code> and press {{key|TAB}} to auto-create a method. (There doesn't seem to be an equivalent feature in MonoDevelop.)
  
==Events==
+
<samp>sender</samp> is part of the C# event pattern, and is unused by SMAPI.
The available events are documented below.
 
  
===Content===
+
===How do events fit into the game?===
<tt>ContentEvents</tt> are raised when the game loads content from its XNB files or changes locale.
+
Events are raised on each game tick (when the game updates its state and renders to the screen), which is 60 times per second. An event may be raised multiple times (''e.g.,'' if the player pressed two keys simultaneously), but most events won't be raised 60 times per second (''e.g.,'' players are unlikely to be pressing 60 buttons per second).
{| class="wikitable"
 
|-
 
! event !! summary
 
|-
 
| AfterLocaleChanged || Raised after the content language changes.
 
|}
 
  
===Control===
+
Your event handlers are run ''synchronously'': the game is paused and no other mod's code will run simultaneously, so there's no risk of conflicting changes. Since code runs very quickly, players won't notice any delay unless your code is unusually slow. That said, when using frequent events like <samp>UpdateTicked</samp> or <samp>Rendered</samp>, you should cache expensive operations (like loading an asset) instead of repeating them in each tick to avoid impacting performance.
<tt>ControlEvents</tt> are raised when the player uses a controller, keyboard, or mouse. They're raised before the game handles the input, so it's possible to selectively prevent the game from responding to it. (That's beyond the scope of this guide, but it involves overwriting <tt>Game1.oldKBState</tt>, <tt>Game1.oldMouseState</tt>, and <tt>Game1.oldPadState</tt>.)
 
  
Most of these events are split into two variants, <tt>XPressed</tt> and <tt>XReleased</tt>. The <tt>Pressed</tt> variant is raised when the player presses the button (holding the button down only triggers the event once), and the <tt>Released</tt> variant is raised when they release it.
+
===What if a mod changes what the event was raised for?===
 +
Events are raised based on a snapshot of the game state. That's usually ''but not necessarily'' the current game state.
  
'''Note:''' mods won't receive input sent to the chatbox, or the toggle-chatbox key.
+
For example, consider this case:
 +
# The <samp>GameMenu</samp> opens.
 +
# SMAPI raises the <samp>MenuChanged</samp> event, which mods A and B listen to.
 +
# Mod A receives the event and closes the menu.
 +
# Mod B receives the event.
  
{| class="wikitable"
+
Each mod is still handling the <samp>MenuChanged</samp> event for the opened menu, even though the first mod closed it. SMAPI will raise a new <samp>MenuChanged</samp> event for the closed menu on the next tick.
|-
 
! event !! summary
 
|-
 
| ControllerButtonPressed<br />ControllerButtonReleased || Raised after the player pressed/released a button on a gamepad or controller. These events aren't raised for trigger buttons.
 
|-
 
| ControllerTriggerPressed<br />ControllerTriggerReleased || Raised after the player pressed/released a trigger button on a gamepad or controller.
 
|-
 
| KeyPressed<br />KeyReleased || Raised after the player pressed/released a keyboard key.
 
|-
 
| KeyboardChanged || Raised after the game's <tt>KeyboardState</tt> changed. That happens when the player presses or releases a key.
 
|-
 
| MouseChanged || Raised after the game's <tt>MouseState</tt> changed. That happens when the player moves the mouse, scrolls the mouse wheel, or presses/releases a button.
 
|}
 
  
===Game===
+
This rarely affects mods, but it's something to keep in mind if you need the current state (''e.g.,'' check <samp>Game1.activeClickableMenu</samp> instead of <samp>e.NewMenu</samp>).
<tt>GameEvents</tt> are raised when the game changes state.
 
  
{| class="wikitable"
+
==Events==
|-
+
The available events are documented below.
! event !! summary
 
|-
 
| FirstUpdateTick || Raised after the game first updates its state. This happens once per game session (unrelated to loading saves).
 
|-
 
| UpdateTick || Raised when the game updates its state (≈60 times per second).
 
|-
 
| SecondUpdateTick || Raised every other tick (≈30 times per second).
 
|-
 
| FourthUpdateTick || Raised every fourth tick (≈15 times per second).
 
|-
 
| EighthUpdateTick || Raised every eighth tick (≈8 times per second).
 
|-
 
| QuarterSecondTick || Raised every 15th tick (≈4 times per second).
 
|-
 
| HalfSecondTick || Raised every 30th tick (≈twice per second).
 
|-
 
| OneSecondTick || Raised every 60th tick (≈once per second).
 
|}
 
  
===Graphics===
+
===Content===
<tt>GraphicsEvents</tt> are raised during the game's draw loop, when the game is rendering content to the window.
+
<samp>this.Helper.Events.Content</samp> has events related to assets loaded from the content pipeline.
  
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! event !! summary
+
! event
|-
+
! summary
| OnPreRenderEvent<br />OnPostRenderEvent || Raised before and after drawing the world to the screen.
+
{{/event
|-
+
|group = Content
| OnPreRenderGuiEvent<br />OnPostRenderGuiEvent || When a menu is open (<tt>Game1.activeClickableMenu != null</tt>), raised before and after drawing that menu to the screen. This includes the game's internal menus like the title screen.
+
|name  = AssetRequested
|-
+
|desc  = Raised when an asset is being requested from the content pipeline. The asset isn't necessarily being loaded yet (''e.g.'' the game may be checking if it exists).
| OnPreRenderHudEvent<br />OnPostRenderHudEvent || Raised before and after drawing the HUD (item toolbar, clock, etc) to the screen. The HUD is available at this point, but not necessarily visible. (For example, the event is called even if a menu is open.)
 
|-
 
| Resize || Raised after the game window is resized.
 
|}
 
  
===Input===
+
You can register the changes you want to apply using the event arguments below; they'll be applied when the asset is actually loaded. See the [[Modding:Modder Guide/APIs/Content|content API]] for more info.
<tt>InputEvents</tt> are raised when the player uses a controller, keyboard, or mouse.
 
  
'''Note:''' mods won't receive input sent to the chatbox, or the toggle-chatbox key.
+
If the asset is requested multiple times in the same tick (e.g. once to check if it exists and once to load it), SMAPI might only raise the event once and reuse the cached result.
  
<table class="wikitable">
+
|arg name 1 = <samp>e.Name</samp>
<tr>
+
|arg type 1 = <samp>IAssetName</samp>
<th>event</th>
+
|arg desc 1 = The name of the asset being requested, including the locale code if any (like the <samp>.fr-FR</samp> in <samp>Data/Bundles.fr-FR</samp>). This includes utility methods to parse the value, like <code>e.Name.IsEquivalentTo("Portraits/Abigail")</code> (which handles any differences in formatting for you).
<th>summary</th>
 
</tr>
 
<tr>
 
<td>ButtonPressed<br />ButtonReleased</td>
 
<td>Raised after the player pressed/released a keyboard, mouse, or controller button. The event arguments include:
 
* the button pressed (as an <tt>SButton</tt> constant);
 
* whether the input counts as a 'click' (including the controller equivalent);
 
* the cursor position on the screen;
 
* the tile under the cursor;
 
* the 'grab tile' (i.e. the tile the game considers to be clicked).
 
  
You can also...
+
|arg name 2 = <samp>e.NameWithoutLocale</samp>
* get the equivalent XNA button constants using <tt>button.TryGetKeyboard(…)</tt> and <tt>button.TryGetController(…)</tt>;
+
|arg type 2 = <samp>IAssetName</samp>
* prevent the game (but not other mods) from handling the input using <tt>eventArgs.SuppressButton()</tt>.</td>
+
|arg desc 2 = Equivalent to <samp>e.Name</samp> but without the locale code (if any). For example, if <samp>e.Name</samp> is <samp>Data/Bundles.fr-FR</samp>, this field will contain <samp>Data/Bundles</samp>.
</tr>
 
</table>
 
  
===Location===
+
|arg name 3 = <samp>e.LoadFrom(...)</samp>
<tt>LocationEvents</tt> are raised when the player transitions between game locations, a location is added or removed, or the objects in the current location change.
+
|arg type 3 = ''method''
 +
|arg desc 3 = Call this method to provide the initial instance for the asset, instead of trying to load it from the game's <samp>Content</samp> folder. For example:
 +
<syntaxhighlight lang="c#">
 +
e.LoadFrom(() => this.Helper.Content.Load<Texture2D>("assets/portraits.png"), AssetLoadPriority.Medium);
 +
</syntaxhighlight>
  
:{| class="wikitable"
+
Usage notes:
|-
+
* The asset doesn't need to exist in the game's <samp>Content</samp> folder. If any mod loads the asset, the game will see it as an existing asset as if it was in that folder.
! event !! summary
+
* Each asset can logically only have one initial instance. If multiple loads apply at the same time, SMAPI will raise an error and ignore all of them. If you're making changes to the existing asset instead of replacing it, you should use the <samp>Edit</samp> method instead to avoid those limitations and improve mod compatibility.
|-
 
| LocationsChanged || Raised after a game location is added or removed (including building interiors). Handlers are passed lists of added/removed locations.
 
|-
 
| BuildingsChanged || Raised after buildings is added/removed in any location. Handlers are given the location, and lists of added/removed buildings.
 
|-
 
| ObjectsChanged || Raised after objects are added/removed in any location (including building interiors). Handlers are given the location, and lists of added/removed objects.
 
|}
 
  
===Menu===
+
|arg name 4 = <samp>e.LoadFromModFile<T>(...)</samp>
<tt>MenuEvents</tt> are raised when a game menu is opened or closed (including internal menus like the title screen).
+
|arg type 4 = ''method''
 +
|arg desc 4 = Call this method to provide the initial instance for the asset by loading a file from your mod folder. For example:
 +
<syntaxhighlight lang="c#">
 +
e.LoadFromModFile<Texture2D>("assets/portraits.png", AssetLoadPriority.Medium);
 +
</syntaxhighlight>
  
{| class="wikitable"
+
See usage notes on <samp>e.LoadFrom</samp>.
|-
 
! event !! summary
 
|-
 
| MenuChanged || Raised after a game menu is opened or replaced with another menu. This event is not invoked when a menu is closed. Handlers are given the previous menu (if any) and new menu (if any).
 
|-
 
| MenuClosed || Raised after a game menu is closed. Handlers are given the previous menu.
 
|}
 
  
===Mine===
+
|arg name 5 = <samp>e.Edit(...)</samp>
<tt>MineEvents</tt> are raised when something happens in [[The Mines]].
+
|arg type 5 = ''method''
 +
|arg desc 5 = Call this method to apply edits to the asset after it's loaded. For example:
 +
<syntaxhighlight lang="c#">
 +
e.Edit(asset =>
 +
{
 +
    Texture2D ribbon = this.Helper.Content.Load<Texture2D>("assets/ribbon.png");
 +
    asset.AsImage().PatchImage(source: overlay, patchMode: PatchMode.Overlay);
 +
});
 +
</syntaxhighlight>
  
{| class="wikitable"
+
Usage notes:
|-
+
* Editing an asset which doesn't exist has no effect. This is applied after the asset is loaded from the game's <samp>Content</samp> folder, or from any mod's <samp>LoadFrom</samp> or <samp>LoadFromModFile</samp>.
! event !! summary
+
* You can apply any number of edits to the asset. Each edit will be applied on top of the previous one (i.e. it'll see the merged asset from all previous edits as its input).
|-
+
}}
| MineLevelChanged || Raised after the player warps to a new level of the mine. Handlers are passed the previous and new mine level as arguments.
+
{{/event
|}
+
|group = Content
 +
|name  = AssetsInvalidated
 +
|desc  = Raised after one or more assets were [[Modding:Modder Guide/APIs/Content#Cache invalidation|invalidated from the content cache]] by a mod, so they'll be reloaded next time they're requested. If the assets will be reloaded or propagated automatically, this event is raised before that happens.
  
===Multiplayer===
+
Next time the asset is loaded (including for asset propagation), the [[#Content.AssetRequested|<samp>AssetRequested</samp>]] event will be raised again.
<tt>MultiplayerEvents</tt> are raised during the multiplayer sync process. These are specialised events; most mods shouldn't use these directly.
 
  
{| class="wikitable"
+
|arg name 1 = <samp>e.Names</samp>
|-
+
|arg type 1 = <samp>IReadOnlySet<IAssetName></samp>
! event !! summary
+
|arg desc 1 = The asset names that were invalidated, including the locale code if any (like the <samp>.fr-FR</samp> in <samp>Data/Bundles.fr-FR</samp>). These include utility methods to parse the values, like <code>name.IsEquivalentTo("Portraits/Abigail")</code> (which handles any differences in formatting for you).
|-
 
| BeforeMainSync<br />AfterMainSync || Raised before and after the game fetches data from other players and syncs the world to match.
 
|-
 
| BeforeMainBroadcast<br />AfterMainBroadcast || Raised before/after the game sends world data to other players. This event is raised for the main world broadcast, but a few specialised broadcasts (particularly sprite broadcasts) occur outside this event.
 
|}
 
  
===Player===
+
|arg name 2 = <samp>e.NamesWithoutLocale</samp>
<tt>PlayerEvents</tt> are raised when the player data changes.
+
|arg type 2 = <samp>IReadOnlySet<IAssetName></samp>
 +
|arg desc 2 = Equivalent to <samp>e.Names</samp> but without the locale code (if any). For example, if <samp>e.Names</samp> contains <samp>Data/Bundles.fr-FR</samp>, this field will contain <samp>Data/Bundles</samp>.
 +
}}
 +
{{/event
 +
|group = Content
 +
|name  = AssetReady
 +
|desc  = Raised after an asset is loaded by the content pipeline, after any mod edits specified via [[#Content.AssetRequested|<samp>AssetRequested</samp>]] have been applied.
  
{| class="wikitable"
+
This event is only raised if something requested the asset from the content pipeline. Invalidating an asset from the content cache won't necessarily reload it automatically.
|-
 
! event !! summary
 
|-
 
| InventoryChanged || Raised after the current player's inventory changes in any way (added or removed item, sorted, etc).
 
|-
 
| LeveledUp || Raised after the current player levels up a skill. This happens as soon as they level up, not when the game notifies the player after their character goes to bed.
 
|-
 
| Warped || Raised after the current player moves to a new location.
 
|}
 
  
===Save===
+
|arg name 1 = <samp>e.Name</samp>
<tt>SaveEvents</tt> are raised when the player saves or loads the game.
+
|arg type 1 = <samp>IAssetName</samp>
 +
|arg desc 1 = The name of the asset that was loaded, including the locale code if any (like the <samp>.fr-FR</samp> in <samp>Data/Bundles.fr-FR</samp>). This includes utility methods to parse the value, like <code>e.Name.IsEquivalentTo("Portraits/Abigail")</code> (which handles any differences in formatting for you).
  
{| class="wikitable"
+
|arg name 2 = <samp>e.NameWithoutLocale</samp>
|-
+
|arg type 2 = <samp>IAssetName</samp>
! event !! summary
+
|arg desc 2 = Equivalent to <samp>e.Name</samp> but without the locale code (if any). For example, if <samp>e.Name</samp> is <samp>Data/Bundles.fr-FR</samp>, this field will contain <samp>Data/Bundles</samp>.
|-
+
}}
| BeforeCreate<br />AfterCreate || Raised before and after the game creates the save file (after the new-game intro). The save won't be written until all mods have finished handling this event.
+
{{/event
|-
+
|group = Content
| AfterLoad || Raised after the player loads a saved game (or starts a new save). The world is ready for mods to modify at this point.
+
|name  = LocaleChanged
|-
+
|desc  = Raised after the game language changes. For non-English players, this may be raised during startup when the game switches to the previously selected language.
| BeforeSave<br />AfterSave || Raised before and after the game updates the save file. Not raised for the initial creation. The save won't be written until all mods have finished handling this event.
 
|-
 
| AfterReturnToTitle || Raised after the player exits to the title screen.
 
|}
 
  
===Time===
+
|arg name 1 = <samp>e.OldLanguage</samp>
<tt>TimeEvents</tt> are raised when the in-game date or time changes.
+
|arg type 1 = <samp>LanguageCode</samp>
 +
|arg desc 1 = The previous value for the game's language enum. For a custom language, this is always <samp>LanguageCode.mod</samp>.
  
{| class="wikitable"
+
|arg name 2 = <samp>e.OldLocale</samp>
|-
+
|arg type 2 = <samp>string</samp>
! event !! summary
+
|arg desc 2 = The previous value for the locale code. This is the locale code as it appears in asset names, like <samp>fr-FR</samp> in <samp>Maps/springobjects.fr-FR</samp>. The locale code for English is an empty string.
|-
 
| AfterDayStarted || Raised after the game begins a new day, including when loading a save.
 
|-
 
| TimeOfDayChanged || Raised after the in-game clock changes.
 
|}
 
  
===Specialised===
+
|arg name 3 = <samp>e.NewLanguage</samp>
<tt>SpecialisedEvents</tt> serve specialised edge cases and should not be used by most mods. Mods using specialised events will be flagged in the SMAPI console as potentially impacting the game stability.
+
|arg type 3 = <samp>LanguageCode</samp>
 +
|arg desc 3 = The new value for the game's language enum. See notes on <samp>OldLanguage</samp>.
  
{{collapse|expand for details|content=
+
|arg name 4 = <samp>e.NewLocale</samp>
<table class="wikitable">
+
|arg type 4 = <samp>string</samp>
  <tr>
+
|arg desc 4 = The previous value for the locale code. See notes on <samp>OldLocale</samp>.
    <th>event</th>
 
    <th>summary</th>
 
  </tr>
 
  <tr>
 
    <td>UnvalidatedUpdateTick</td>
 
    <td>Raised after the game updates its state (≈60 times per second), regardless of normal SMAPI validation. This event is not thread-safe and may be invoked while game logic is running asynchronously. Changes to game state in this method may crash the game or corrupt an in-progress save. '''Do not use this event unless you're fully aware of the context in which your code will be run. Using this event will trigger a warning in the SMAPI console.'''</td>
 
  </tr>
 
</table>
 
 
}}
 
}}
 
+
|}
==Events (SMAPI 3.0)==
 
{{SMAPI upcoming|3.0}}
 
SMAPI 3.0 will introduce a new event system that's much more powerful, flexible, and consistent. You can try these events now, but '''these are preview prototypes and may change at any time'''. See [[Modding:Migrate to SMAPI 3.0]] for details.
 
  
 
===Display===
 
===Display===
<tt>this.Helper.Events.Displays</tt> has events linked to UI and drawing to the screen.
+
<samp>this.Helper.Events.Display</samp> has events linked to UI and drawing to the screen.
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 257: Line 189:
 
  |desc  = Raised after a game menu is opened, closed, or replaced.
 
  |desc  = Raised after a game menu is opened, closed, or replaced.
  
  |arg name 2 = <tt>e.OldMenu</tt>
+
  |arg name 2 = <samp>e.OldMenu</samp>
  |arg type 2 = <tt>IClickableMenu</tt>
+
  |arg type 2 = <samp>IClickableMenu</samp>
  |arg desc 2 = The old menu instance (or <tt>null</tt> if none).
+
  |arg desc 2 = The old menu instance (or <samp>null</samp> if none).
  
  |arg name 1 = <tt>e.NewMenu</tt>
+
  |arg name 1 = <samp>e.NewMenu</samp>
  |arg type 1 = <tt>IClickableMenu</tt>
+
  |arg type 1 = <samp>IClickableMenu</samp>
  |arg desc 1 = The new menu instance (or <tt>null</tt> if none).
+
  |arg desc 1 = The new menu instance (or <samp>null</samp> if none).
 
}}
 
}}
 
{{/event
 
{{/event
Line 270: Line 202:
 
  |desc  = Raised before the game draws anything to the screen in a draw tick, as soon as the sprite batch is opened. The sprite batch may be closed and reopened multiple times after this event is called, but it's only raised once per draw tick. This event isn't useful for drawing to the screen, since the game will draw over it.
 
  |desc  = Raised before the game draws anything to the screen in a draw tick, as soon as the sprite batch is opened. The sprite batch may be closed and reopened multiple times after this event is called, but it's only raised once per draw tick. This event isn't useful for drawing to the screen, since the game will draw over it.
  
Limitations:
+
  |arg name 1 = <samp>e.SpriteBatch</samp>
* The event isn't raised during some cutscenes or minigames (e.g. the [[Sebastian#Six Hearts|board game scene]]) due to limitations in the game design.
+
  |arg type 1 = <samp>SpriteBatch</samp>
 
 
  |arg name 1 = <tt>e.SpriteBatch</tt>
 
  |arg type 1 = <tt>SpriteBatch</tt>
 
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
}}
 
}}
Line 280: Line 209:
 
  |group = Display
 
  |group = Display
 
  |name  = Rendered
 
  |name  = Rendered
  |desc  = Raised after the game draws to the sprite patch in a draw tick, just before the final sprite batch is rendered to the screen. Since the game may open/close the sprite batch multiple times in a draw tick, the sprite batch may not contain everything being drawn and some things may already be rendered to the screen. Content drawn to the sprite batch at this point will be drawn over all vanilla content (including menus, HUD, and cursor).
+
  |desc  = Raised after the game draws to the sprite batch in a draw tick, just before the final sprite batch is rendered to the screen. Since the game may open/close the sprite batch multiple times in a draw tick, the sprite batch may not contain everything being drawn and some things may already be rendered to the screen. Content drawn to the sprite batch at this point will be drawn over all vanilla content (including menus, HUD, and cursor).
  
Limitations:
+
  |arg name 1 = <samp>e.SpriteBatch</samp>
* The event isn't raised during some cutscenes or minigames (e.g. the [[Sebastian#Six Hearts|board game scene]]) due to limitations in the game design.
+
  |arg type 1 = <samp>SpriteBatch</samp>
 
 
  |arg name 1 = <tt>e.SpriteBatch</tt>
 
  |arg type 1 = <tt>SpriteBatch</tt>
 
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
}}
 
}}
Line 294: Line 220:
 
  |desc  = Raised before the game world is drawn to the screen. This event isn't useful for drawing to the screen, since the game will draw over it.
 
  |desc  = Raised before the game world is drawn to the screen. This event isn't useful for drawing to the screen, since the game will draw over it.
  
  |arg name 1 = <tt>e.SpriteBatch</tt>
+
  |arg name 1 = <samp>e.SpriteBatch</samp>
  |arg type 1 = <tt>SpriteBatch</tt>
+
  |arg type 1 = <samp>SpriteBatch</samp>
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
}}
 
}}
Line 301: Line 227:
 
  |group = Display
 
  |group = Display
 
  |name  = RenderedWorld
 
  |name  = RenderedWorld
  |desc  = Raised after the game world is drawn to the sprite patch, before it's rendered to the screen. Content drawn to the sprite batch at this point will be drawn over the world, but under any active menu, HUD elements, or cursor.
+
  |desc  = Raised after the game world is drawn to the sprite batch, before it's rendered to the screen. Content drawn to the sprite batch at this point will be drawn over the world, but under any active menu, HUD elements, or cursor.
  
  |arg name 1 = <tt>e.SpriteBatch</tt>
+
  |arg name 1 = <samp>e.SpriteBatch</samp>
  |arg type 1 = <tt>SpriteBatch</tt>
+
  |arg type 1 = <samp>SpriteBatch</samp>
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
}}
 
}}
Line 310: Line 236:
 
  |group = Display
 
  |group = Display
 
  |name  = RenderingActiveMenu
 
  |name  = RenderingActiveMenu
  |desc  = When a menu is open (<tt>Game1.activeClickableMenu != null</tt>), raised before that menu is drawn to the screen. This includes the game's internal menus like the title screen. Content drawn to the sprite batch at this point will appear under the menu.
+
  |desc  = When a menu is open (<samp>Game1.activeClickableMenu != null</samp>), raised before that menu is drawn to the screen. This includes the game's internal menus like the title screen. Content drawn to the sprite batch at this point will appear under the menu.
  
  |arg name 1 = <tt>e.SpriteBatch</tt>
+
  |arg name 1 = <samp>e.SpriteBatch</samp>
  |arg type 1 = <tt>SpriteBatch</tt>
+
  |arg type 1 = <samp>SpriteBatch</samp>
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
}}
 
}}
Line 319: Line 245:
 
  |group = Display
 
  |group = Display
 
  |name  = RenderedActiveMenu
 
  |name  = RenderedActiveMenu
  |desc  = When a menu is open (<tt>Game1.activeClickableMenu != null</tt>), raised after that menu is drawn to the sprite batch but before it's rendered to the screen. Content drawn to the sprite batch at this point will appear over the menu and menu cursor.
+
  |desc  = When a menu is open (<samp>Game1.activeClickableMenu != null</samp>), raised after that menu is drawn to the sprite batch but before it's rendered to the screen. Content drawn to the sprite batch at this point will appear over the menu and menu cursor.
  
  |arg name 1 = <tt>e.SpriteBatch</tt>
+
  |arg name 1 = <samp>e.SpriteBatch</samp>
  |arg type 1 = <tt>SpriteBatch</tt>
+
  |arg type 1 = <samp>SpriteBatch</samp>
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
}}
 
}}
Line 328: Line 254:
 
  |group = Display
 
  |group = Display
 
  |name  = RenderingHud
 
  |name  = RenderingHud
  |desc  = Raised before drawing the HUD (item toolbar, clock, etc) to the screen. The vanilla HUD may be hidden at this point (e.g. because a menu is open). Content drawn to the sprite batch at this point will appear under the HUD.
+
  |desc  = Raised before drawing the HUD (item toolbar, clock, etc) to the screen. The vanilla HUD may be hidden at this point (''e.g.,'' because a menu is open). Content drawn to the sprite batch at this point will appear under the HUD.
  
  |arg name 1 = <tt>e.SpriteBatch</tt>
+
  |arg name 1 = <samp>e.SpriteBatch</samp>
  |arg type 1 = <tt>SpriteBatch</tt>
+
  |arg type 1 = <samp>SpriteBatch</samp>
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
}}
 
}}
Line 337: Line 263:
 
  |group = Display
 
  |group = Display
 
  |name  = RenderedHud
 
  |name  = RenderedHud
  |desc  = Raised after drawing the HUD (item toolbar, clock, etc) to the sprite batch, but before it's rendered to the screen. The vanilla HUD may be hidden at this point (e.g. because a menu is open). Content drawn to the sprite batch at this point will appear over the HUD.
+
  |desc  = Raised after drawing the HUD (item toolbar, clock, etc) to the sprite batch, but before it's rendered to the screen. The vanilla HUD may be hidden at this point (''e.g.,'' because a menu is open). Content drawn to the sprite batch at this point will appear over the HUD.
  
  |arg name 1 = <tt>e.SpriteBatch</tt>
+
  |arg name 1 = <samp>e.SpriteBatch</samp>
  |arg type 1 = <tt>SpriteBatch</tt>
+
  |arg type 1 = <samp>SpriteBatch</samp>
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
  |arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 
}}
 
}}
Line 348: Line 274:
 
  |desc  = Raised after the game window is resized.
 
  |desc  = Raised after the game window is resized.
  
  |arg name 1 = <tt>e.OldSize</tt>
+
  |arg name 1 = <samp>e.OldSize</samp>
  |arg type 1 = <tt>Point</tt>
+
  |arg type 1 = <samp>Point</samp>
  |arg desc 1 = The previous window width (<tt>e.OldSize.X</tt>) and height (<tt>e.OldSize.Y</tt>).
+
  |arg desc 1 = The previous window width (<samp>e.OldSize.X</samp>) and height (<samp>e.OldSize.Y</samp>).
  
  |arg name 2 = <tt>e.NewSize</tt>
+
  |arg name 2 = <samp>e.NewSize</samp>
  |arg type 2 = <tt>Point</tt>
+
  |arg type 2 = <samp>Point</samp>
  |arg desc 2 = The new window width (<tt>e.NewSize.X</tt>) and height (<tt>e.NewSize.Y</tt>).
+
  |arg desc 2 = The new window width (<samp>e.NewSize.X</samp>) and height (<samp>e.NewSize.Y</samp>).
 +
}}
 +
{{/event
 +
|group = Display
 +
|name  = RenderingStep
 +
|desc  = ''(Specialized)'' Raised before the game draws a specific step in the rendering cycle. This gives more granular control over render logic, but is more vulnerable to changes in game updates. Consider using one of the other render events if possible.
 +
 
 +
|arg name 1 = <samp>e.SpriteBatch</samp>
 +
|arg type 1 = <samp>SpriteBatch</samp>
 +
|arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 +
 
 +
|arg name 2 = <samp>e.Step</samp>
 +
|arg type 2 = <samp>RenderSteps</samp>
 +
|arg desc 2 = The step being rendered in the draw cycle.
 +
}}
 +
{{/event
 +
|group = Display
 +
|name  = RenderedStep
 +
|desc  = ''(Specialized)'' Raised after the game draws a specific step in the rendering cycle.  This gives more granular control over render logic, but is more vulnerable to changes in game updates. Consider using one of the other render events if possible.
 +
 
 +
|arg name 1 = <samp>e.SpriteBatch</samp>
 +
|arg type 1 = <samp>SpriteBatch</samp>
 +
|arg desc 1 = The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
 +
 
 +
|arg name 2 = <samp>e.Step</samp>
 +
|arg type 2 = <samp>RenderSteps</samp>
 +
|arg desc 2 = The step being rendered in the draw cycle.  
 
}}
 
}}
 
|}
 
|}
  
 
===Game loop===
 
===Game loop===
<tt>this.Helper.Events.GameLoop</tt> has events linked to the game's update loop. The update loop runs roughly ≈60 times/second to run game logic like state changes, action handling, etc. These are often useful, but you should consider semantic events like <tt>Input</tt> where applicable.
+
<samp>this.Helper.Events.GameLoop</samp> has events linked to the game's update loop. The update loop runs roughly ≈60 times/second to run game logic like state changes, action handling, etc. These are often useful, but you should consider semantic events like <samp>Input</samp> where applicable.
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 375: Line 327:
 
  |desc  = Raised before/after the game state is updated (≈60 times per second).
 
  |desc  = Raised before/after the game state is updated (≈60 times per second).
  
  |arg name 1 = <tt>e.Ticks</tt>
+
  |arg name 1 = <samp>e.Ticks</samp>
  |arg type 1 = <tt>int</tt>
+
  |arg type 1 = <samp>int</samp>
 
  |arg desc 1 = The number of ticks elapsed since the game started, including the current tick.
 
  |arg desc 1 = The number of ticks elapsed since the game started, including the current tick.
  
  |arg name 2 = <tt>e.IsOneSecond</tt>
+
  |arg name 2 = <samp>e.IsOneSecond</samp>
  |arg type 2 = <tt>bool</tt>
+
  |arg type 2 = <samp>bool</samp>
  |arg desc 2 = Whether <tt>e.TicksElapsed</tt> is a multiple of 60, which happens approximately once per second.
+
  |arg desc 2 = Whether <samp>e.TicksElapsed</samp> is a multiple of 60, which happens approximately once per second.
  
  |arg name 3 = <tt>e.IsMultipleOf(int number)</tt>
+
  |arg name 3 = <samp>e.IsMultipleOf(int number)</samp>
  |arg type 3 = ''method'' returns <tt>bool</tt>
+
  |arg type 3 = ''method'' returns <samp>bool</samp>
  |arg desc 3 = Whether <tt>e.TicksElapsed</tt> is a multiple of the given number. This is mainly useful if you want to run logic intermittently (e.g. <code>e.IsMultipleOf(30)</code> for every half-second).
+
  |arg desc 3 = Whether <samp>e.TicksElapsed</samp> is a multiple of the given number. This is mainly useful if you want to run logic intermittently (''e.g.,'' <code>e.IsMultipleOf(30)</code> for every half-second).
 +
}}
 +
{{/event
 +
|group = GameLoop
 +
|name  = OneSecondUpdateTicking, OneSecondUpdateTicked
 +
|desc  = Raised before/after the game state is updated, once per second.
 +
 
 +
|arg name 1 = <samp>e.Ticks</samp>
 +
|arg type 1 = <samp>int</samp>
 +
|arg desc 1 = The number of ticks elapsed since the game started, including the current tick.
 +
 
 +
|arg name 3 = <samp>e.IsMultipleOf(int number)</samp>
 +
|arg type 3 = ''method'' returns <samp>bool</samp>
 +
|arg desc 3 = Whether <samp>e.TicksElapsed</samp> is a multiple of the given number. This is mainly useful if you want to run logic intermittently (''e.g.,'' <code>e.IsMultipleOf(120)</code> for every two seconds).
 
}}
 
}}
 
{{/event
 
{{/event
 
  |group = GameLoop
 
  |group = GameLoop
 
  |name  = SaveCreating, SaveCreated
 
  |name  = SaveCreating, SaveCreated
  |desc  = Raised before/after the game creates the save file (after the new-game intro). The save won't be written until all mods have finished handling this event. This is a somewhat specialised event, since the world isn't fully initialised at this point; in most cases you should use [[#GameLoop.DayStarted|<tt>DayStarted</tt>]], [[#GameLoop.Saving|<tt>Saving</tt>]], [[#GameLoop.Saved|<tt>Saved</tt>]] instead.
+
  |desc  = Raised before/after the game creates the save file (after the new-game intro). The save won't be written until all mods have finished handling this event. This is a somewhat specialised event, since the world isn't fully initialised at this point; in most cases you should use [[#GameLoop.DayStarted|<samp>DayStarted</samp>]], [[#GameLoop.Saving|<samp>Saving</samp>]], or [[#GameLoop.Saved|<samp>Saved</samp>]] instead.
 
}}
 
}}
 
{{/event
 
{{/event
 
  |group = GameLoop
 
  |group = GameLoop
 
  |name = Saving, Saved
 
  |name = Saving, Saved
  |desc = Raised before/after the game writes data to save file (except [[#GameLoop.SaveCreating|the initial save creation]]). The save won't be written until all mods have finished handling this event.
+
  |desc = Raised before/after the game writes data to save file (except [[#GameLoop.SaveCreating|the initial save creation]]). The save won't be written until all mods have finished handling this event. This is also raised for farmhands in multiplayer.
 
}}
 
}}
 
{{/event
 
{{/event
 
  |group = GameLoop
 
  |group = GameLoop
 
  |name  = SaveLoaded
 
  |name  = SaveLoaded
  |desc  = Raised before/after the game reads data from a save file and initialises the world. This event isn't raised after saving; if you want to do something at the start of each day, see [[#GameLoop.DayStarted|<tt>DayStarted</tt>]] instead.
+
  |desc  = Raised after loading a save (including the first day after creating a new save), or connecting to a multiplayer world. This happens right before <samp>DayStarted</samp>; at this point the save file is read and <samp>[[Modding:Modder Guide/APIs/Utilities#Context|Context.IsWorldReady]]</samp> is true.
 +
 
 +
This event isn't raised after saving; if you want to do something at the start of each day, see [[#GameLoop.DayStarted|<samp>DayStarted</samp>]] instead.  
 
}}
 
}}
 
{{/event
 
{{/event
 
  |group = GameLoop
 
  |group = GameLoop
 
  |name  = DayStarted
 
  |name  = DayStarted
  |desc  = Raised after a new in-game day starts. Everything has already been initialised at this point. (To run code before the game sets up the day, see [[#GameLoop.DayEnding|<tt>DayEnding</tt>]] instead.)
+
  |desc  = Raised after a new in-game day starts, or after connecting to a multiplayer world. Everything has already been initialised at this point. (To run code before the game sets up the day, see [[#GameLoop.DayEnding|<samp>DayEnding</samp>]] instead.)
 
}}
 
}}
 
{{/event
 
{{/event
 
  |group = GameLoop
 
  |group = GameLoop
 
  |name  = DayEnding
 
  |name  = DayEnding
  |desc  = Raised before the game ends the current day. This happens before it starts setting up the next day and before [[#GameLoop.Saving|<tt>Saving</tt>]].
+
  |desc  = Raised before the game ends the current day. This happens before it starts setting up the next day and before [[#GameLoop.Saving|<samp>Saving</samp>]].
 
}}
 
}}
 
{{/event
 
{{/event
Line 417: Line 384:
 
  |desc  = Raised after the in-game clock time changes, which happens in intervals of ten in-game minutes.
 
  |desc  = Raised after the in-game clock time changes, which happens in intervals of ten in-game minutes.
  
  |arg name 1 = <tt>e.OldTime</tt>
+
  |arg name 1 = <samp>e.OldTime</samp>
  |arg type 1 = <tt>int</tt>
+
  |arg type 1 = <samp>int</samp>
 
  |arg desc 1 = The previous time of day in 24-hour notation (like 1600 for 4pm). The clock time resets when the player sleeps, so 2am (before sleeping) is 2600.
 
  |arg desc 1 = The previous time of day in 24-hour notation (like 1600 for 4pm). The clock time resets when the player sleeps, so 2am (before sleeping) is 2600.
  
  |arg name 2 = <tt>e.NewTime</tt>
+
  |arg name 2 = <samp>e.NewTime</samp>
  |arg type 2 = <tt>int</tt>
+
  |arg type 2 = <samp>int</samp>
 
  |arg desc 2 = The current time of day in 24-hour notation (like 1600 for 4pm). The clock time resets when the player sleeps, so 2am (before sleeping) is 2600.
 
  |arg desc 2 = The current time of day in 24-hour notation (like 1600 for 4pm). The clock time resets when the player sleeps, so 2am (before sleeping) is 2600.
 
}}
 
}}
Line 433: Line 400:
  
 
===Input===
 
===Input===
<tt>this.Helper.Events.Input</tt> has events raised when the player uses a controller, keyboard, or mouse in some way. They can be used with the [[../Input|input API]] to access more info or suppress input.
+
<samp>this.Helper.Events.Input</samp> has events raised when the player uses a controller, keyboard, or mouse in some way. They can be used with the [[../Input|input API]] to access more info or suppress input.
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 439: Line 406:
 
! event
 
! event
 
! summary
 
! summary
 +
{{/event
 +
|group = Input
 +
|name  = ButtonsChanged
 +
|desc  = Raised after the player pressed/released any buttons on the keyboard, mouse, or controller. This includes mouse clicks. If the player pressed/released multiple keys at once, this is only raised once.
 +
 +
|arg name 1 = <samp>e.Pressed</samp>
 +
|arg type 1 = [[Modding:Modder Guide/APIs/Input#SButton|<samp>SButton[]</samp>]]
 +
|arg desc 1 = The buttons that were pressed since the previous tick.
 +
 +
|arg name 2 = <samp>e.Held</samp>
 +
|arg type 2 = [[Modding:Modder Guide/APIs/Input#SButton|<samp>SButton[]</samp>]]
 +
|arg desc 2 = The buttons that were held since the previous tick.
 +
 +
|arg name 3 = <samp>e.Released</samp>
 +
|arg type 3 = [[Modding:Modder Guide/APIs/Input#SButton|<samp>SButton[]</samp>]]
 +
|arg desc 3 = The buttons that were released since the previous tick.
 +
 +
|arg name 4 = <samp>e.Cursor</samp>
 +
|arg type 4 = [[Modding:Modder Guide/APIs/Input#Check cursor position|<samp>ICursorPosition</samp>]]
 +
|arg desc 4 = The cursor position and grab tile.
 +
 +
'''Note:''' mods won't receive input sent to the chatbox.
 +
}}
 
{{/event
 
{{/event
 
  |group = Input
 
  |group = Input
 
  |name  = ButtonPressed, ButtonReleased
 
  |name  = ButtonPressed, ButtonReleased
  |desc  = Raised after the player pressed/released a keyboard, mouse, or controller button. This includes mouse clicks.
+
  |desc  = Raised after the player pressed/released a keyboard, mouse, or controller button. This includes mouse clicks. If the player pressed/released multiple keys at once, this is raised for each button pressed.
  
  |arg name 1 = <tt>e.Button</tt>
+
  |arg name 1 = <samp>e.Button</samp>
  |arg type 1 = [[Modding:Modder Guide/APIs/Input#SButton|<tt>SButton</tt>]]
+
  |arg type 1 = [[Modding:Modder Guide/APIs/Input#SButton|<samp>SButton</samp>]]
 
  |arg desc 1 = The button pressed or released.
 
  |arg desc 1 = The button pressed or released.
  
  |arg name 2 = <tt>e.Cursor</tt>
+
  |arg name 2 = <samp>e.Cursor</samp>
  |arg type 2 = [[Modding:Modder Guide/APIs/Input#Check cursor position|<tt>ICursorPosition</tt>]]
+
  |arg type 2 = [[Modding:Modder Guide/APIs/Input#Check cursor position|<samp>ICursorPosition</samp>]]
 
  |arg desc 2 = The cursor position and grab tile.
 
  |arg desc 2 = The cursor position and grab tile.
  
  |arg name 3 = <tt>e.IsDown</tt>
+
  |arg name 3 = <samp>e.IsDown</samp>
  |arg type 3 = ''method'' returns <tt>bool</tt>
+
  |arg type 3 = ''method'' returns <samp>bool</samp>
 
  |arg desc 3 = Indicates whether a given button is currently pressed.
 
  |arg desc 3 = Indicates whether a given button is currently pressed.
  
  |arg name 4 = <tt>e.IsSuppressed</tt>
+
  |arg name 4 = <samp>e.IsSuppressed</samp>
  |arg type 4 = ''method'' returns <tt>bool</tt>
+
  |arg type 4 = ''method'' returns <samp>bool</samp>
 
  |arg desc 4 = A method which indicates whether a given button was suppressed by a mod, so the game itself won't see it.
 
  |arg desc 4 = A method which indicates whether a given button was suppressed by a mod, so the game itself won't see it.
  
Line 467: Line 457:
 
  |desc  = Raised after the player moves the in-game cursor.
 
  |desc  = Raised after the player moves the in-game cursor.
  
  |arg name 1 = <tt>e.OldPosition</tt>
+
  |arg name 1 = <samp>e.OldPosition</samp>
 
  |arg type 1 = [[Modding:Modder Guide/APIs/Input#Check cursor position|ICursorPosition]]
 
  |arg type 1 = [[Modding:Modder Guide/APIs/Input#Check cursor position|ICursorPosition]]
 
  |arg desc 1 = The previous cursor position and grab tile.
 
  |arg desc 1 = The previous cursor position and grab tile.
  
  |arg name 2 = <tt>e.NewPosition</tt>
+
  |arg name 2 = <samp>e.NewPosition</samp>
 
  |arg type 2 = [[Modding:Modder Guide/APIs/Input#Check cursor position|ICursorPosition]]
 
  |arg type 2 = [[Modding:Modder Guide/APIs/Input#Check cursor position|ICursorPosition]]
 
  |arg desc 2 = The current cursor position and grab tile.
 
  |arg desc 2 = The current cursor position and grab tile.
Line 480: Line 470:
 
  |desc  = Raised after the player scrolls the mouse wheel.
 
  |desc  = Raised after the player scrolls the mouse wheel.
  
  |arg name 1 = <tt>e.Position</tt>
+
  |arg name 1 = <samp>e.Position</samp>
 
  |arg type 1 = [[Modding:Modder Guide/APIs/Input#Check cursor position|ICursorPosition]]
 
  |arg type 1 = [[Modding:Modder Guide/APIs/Input#Check cursor position|ICursorPosition]]
 
  |arg desc 1 = The current cursor position and grab tile.
 
  |arg desc 1 = The current cursor position and grab tile.
  
  |arg name 2 = <tt>e.Delta</tt>
+
  |arg name 2 = <samp>e.Delta</samp>
  |arg type 2 = <tt>int</tt>
+
  |arg type 2 = <samp>int</samp>
 
  |arg desc 2 = The amount by which the mouse wheel was scrolled since the last update.
 
  |arg desc 2 = The amount by which the mouse wheel was scrolled since the last update.
  
  |arg name 3 = <tt>e.OldValue</tt><br /><tt>e.NewValue</tt>
+
  |arg name 3 = <samp>e.OldValue</samp><br /><samp>e.NewValue</samp>
  |arg type 3 = <tt>int</tt>
+
  |arg type 3 = <samp>int</samp>
  |arg desc 3 = The previous and current scroll value, cumulative since the game started. Mods should generally use <tt>e.Delta</tt> instead.
+
  |arg desc 3 = The previous and current scroll value, cumulative since the game started. Mods should generally use <samp>e.Delta</samp> instead.
 
}}
 
}}
 
|}
 
|}
  
 
===Multiplayer===
 
===Multiplayer===
<tt>this.Helper.Events.Multiplayer</tt> has events raised for multiplayer messages and connections.
+
<samp>this.Helper.Events.Multiplayer</samp> has events raised for multiplayer messages and connections.
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 504: Line 494:
 
  |group = Multiplayer
 
  |group = Multiplayer
 
  |name  = PeerContextReceived
 
  |name  = PeerContextReceived
  |desc  = Raised after the mod context for a player is received. This happens before the game approves the connection, so the player does not yet exist in the game. This is the earliest point where messages can be sent to the player via SMAPI.
+
  |desc  = Raised after the mod context for a player is received. The event is raised for any player (whether host or farmhand), including when the connecting player doesn't have SMAPI installed. This is the earliest point where messages can be sent to the player via SMAPI.
  
  |arg name 1 = <tt>e.Peer</tt>
+
This happens immediately before the game approves the connection, so the player doesn't exist in the game yet. When connecting to the host, contextual fields like <samp>Game1.IsMasterGame</samp> or <samp>Context.IsMultiplayer</samp> may not be set yet; you can check <samp>e.Peer.IsHost</samp> to know whether the current player is a farmhand, since the host context will always be received first. Assuming another mod doesn't block the connection, the connection will be approved on the next tick.
  |arg type 1 = <tt>IMultiplayerPeer</tt>
+
 
 +
  |arg name 1 = <samp>e.Peer</samp>
 +
  |arg type 1 = <samp>IMultiplayerPeer</samp>
 
  |arg desc 1 = The peer whose context was received (see [[Modding:Modder Guide/APIs/Multiplayer#Get connected player info|Multiplayer#Get connected player info]]).
 
  |arg desc 1 = The peer whose context was received (see [[Modding:Modder Guide/APIs/Multiplayer#Get connected player info|Multiplayer#Get connected player info]]).
 +
}}
 +
{{/event
 +
|group = Multiplayer
 +
|name  = PeerConnected
 +
|desc  = Raised after a connection from another player is approved by the game. The event is raised for any player (whether host or farmhand), including when the connecting player doesn't have SMAPI installed. This happens after <samp>PeerContextReceived</samp>.
 +
 +
The player is connected to the game at this point, so methods like <samp>Game1.server.kick</samp> will work.
 +
 +
|arg name 1 = <samp>e.Peer</samp>
 +
|arg type 1 = <samp>IMultiplayerPeer</samp>
 +
|arg desc 1 = The peer who connected (see [[Modding:Modder Guide/APIs/Multiplayer#Get connected player info|Multiplayer#Get connected player info]]).
 
}}
 
}}
 
{{/event
 
{{/event
Line 515: Line 518:
 
  |desc  = Raised after [[Modding:Modder Guide/APIs/Multiplayer#Send messages|a mod message]] is received over the network.
 
  |desc  = Raised after [[Modding:Modder Guide/APIs/Multiplayer#Send messages|a mod message]] is received over the network.
  
  |arg name 1 = <tt>e.FromPlayerID</tt>
+
  |arg name 1 = <samp>e.FromPlayerID</samp>
  |arg type 1 = <tt>long</tt>
+
  |arg type 1 = <samp>long</samp>
 
  |arg desc 1 = The unique ID of the player from whose computer the message was sent.
 
  |arg desc 1 = The unique ID of the player from whose computer the message was sent.
  
  |arg name 2 = <tt>e.FromModID</tt>
+
  |arg name 2 = <samp>e.FromModID</samp>
  |arg type 2 = <tt>string</tt>
+
  |arg type 2 = <samp>string</samp>
 
  |arg desc 2 = The unique ID of the mod which sent the message.
 
  |arg desc 2 = The unique ID of the mod which sent the message.
  
  |arg name 3 = <tt>e.Type</tt>
+
  |arg name 3 = <samp>e.Type</samp>
  |arg type 3 = <tt>string</tt>
+
  |arg type 3 = <samp>string</samp>
  |arg desc 3 = A message type which you can use to decide whether it's the one you want to handle. This isn't necessarily globally unique, so you should also check the <tt>FromModID</tt> field.
+
  |arg desc 3 = A message type which you can use to decide whether it's the one you want to handle. This isn't necessarily globally unique, so you should also check the <samp>FromModID</samp> field.
  
  |arg name 4 = <tt>e.ReadAs&lt;TModel&gt;()</tt>
+
  |arg name 4 = <samp>e.ReadAs&lt;TModel&gt;()</samp>
  |arg type 4 = ''method'' returns <tt>TModel</tt>
+
  |arg type 4 = ''method'' returns <samp>TModel</samp>
 
  |arg desc 4 = Read the underlying message data into the given model type (like <code>e.ReadAs&lt;MyMessageClass&gt;()</code> or <code>e.ReadAs&lt;string&gt;()</code>). This will return a new instance each time.
 
  |arg desc 4 = Read the underlying message data into the given model type (like <code>e.ReadAs&lt;MyMessageClass&gt;()</code> or <code>e.ReadAs&lt;string&gt;()</code>). This will return a new instance each time.
 
}}
 
}}
Line 536: Line 539:
 
  |desc  = Raised after the connection to a player is severed.
 
  |desc  = Raised after the connection to a player is severed.
  
  |arg name 1 = <tt>e.Peer</tt>
+
  |arg name 1 = <samp>e.Peer</samp>
  |arg type 1 = <tt>IMultiplayerPeer</tt>
+
  |arg type 1 = <samp>IMultiplayerPeer</samp>
 
  |arg desc 1 = The peer whose connection was severed (see [[Modding:Modder Guide/APIs/Multiplayer#Get connected player info|Multiplayer#Get connected player info]]).
 
  |arg desc 1 = The peer whose connection was severed (see [[Modding:Modder Guide/APIs/Multiplayer#Get connected player info|Multiplayer#Get connected player info]]).
 
}}
 
}}
Line 543: Line 546:
  
 
===Player===
 
===Player===
<tt>this.Helper.Events.Player</tt> has events raised when the player data changes.
+
<samp>this.Helper.Events.Player</samp> has events raised when the player data changes.
  
'''Currently these events are only raised for the current player. That will likely change in a future version, so make sure to check <tt>e.Player</tt>, <tt>e.Player.IsLocalPlayer</tt>, or <tt>e.Player.IsMainPlayer</tt> if needed.'''
+
'''Currently these events are only raised for the current player. That will likely change in a future version, so make sure to check <samp>e.IsLocalPlayer</samp> if you only want to handle the current player.'''
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 556: Line 559:
 
  |desc  = Raised after items are added or removed from the player inventory.
 
  |desc  = Raised after items are added or removed from the player inventory.
  
  |arg name 1 = <tt>e.Player</tt>
+
  |arg name 1 = <samp>e.Player</samp>
  |arg type 1 = <tt>Farmer</tt>
+
  |arg type 1 = <samp>Farmer</samp>
 
  |arg desc 1 = The player whose inventory changed.
 
  |arg desc 1 = The player whose inventory changed.
  
  |arg name 2 = <tt>e.Added</tt>
+
  |arg name 2 = <samp>e.Added</samp>
  |arg type 2 = <tt>IEnumerable&lt;Item&gt;</tt>
+
  |arg type 2 = <samp>IEnumerable&lt;Item&gt;</samp>
 
  |arg desc 2 = The added item stacks.
 
  |arg desc 2 = The added item stacks.
  
  |arg name 3 = <tt>e.Removed</tt>
+
  |arg name 3 = <samp>e.Removed</samp>
  |arg type 3 = <tt>IEnumerable&lt;Item&gt;</tt>
+
  |arg type 3 = <samp>IEnumerable&lt;Item&gt;</samp>
 
  |arg desc 3 = The removed item stacks.
 
  |arg desc 3 = The removed item stacks.
  
  |arg name 4 = <tt>e.StackSizeChanged</tt>
+
  |arg name 4 = <samp>e.QuantityChanged</samp>
  |arg type 4 = <tt>IEnumerable&lt;ItemStackSizeChange&gt;</tt>
+
  |arg type 4 = <samp>IEnumerable&lt;ItemStackSizeChange&gt;</samp>
  |arg desc 4 = The item stacks whose quantity changed. Each <tt>ItemStackSizeChange</tt> instance includes <tt>Item</tt> (the affected item stack), <tt>OldSize</tt> (the previous stack size), and <tt>NewSize</tt> (the new stack size).
+
  |arg desc 4 = The item stacks whose quantity changed. Each <samp>ItemStackSizeChange</samp> instance includes <samp>Item</samp> (the affected item stack), <samp>OldSize</samp> (the previous stack size), and <samp>NewSize</samp> (the new stack size).
  
  |arg name 5 = <tt>e.IsLocalPlayer</tt>
+
  |arg name 5 = <samp>e.IsLocalPlayer</samp>
  |arg type 5 = <tt>bool</tt>
+
  |arg type 5 = <samp>bool</samp>
 
  |arg desc 5 = Whether the affected player is the local one.
 
  |arg desc 5 = Whether the affected player is the local one.
 
}}
 
}}
Line 581: Line 584:
 
  |desc  = Raised after a player's skill level changes. When the player levels up normally, this is raised immediately (not when the game notifies the player after they go to bed).
 
  |desc  = Raised after a player's skill level changes. When the player levels up normally, this is raised immediately (not when the game notifies the player after they go to bed).
  
  |arg name 1 = <tt>e.Player</tt>
+
  |arg name 1 = <samp>e.Player</samp>
  |arg type 1 = <tt>Farmer</tt>
+
  |arg type 1 = <samp>Farmer</samp>
 
  |arg desc 1 = The player whose skill level changed.
 
  |arg desc 1 = The player whose skill level changed.
  
  |arg name 2 = <tt>Skill</tt>
+
  |arg name 2 = <samp>e.Skill</samp>
  |arg type 2 = <tt>SkillType</tt>
+
  |arg type 2 = <samp>SkillType</samp>
 
  |arg desc 2 = The skill whose level changed. This is a constant like <code>SkillType.Combat</code>, which can be converted to the game's internal skill ID using <code>(int)e.Skill</code>.
 
  |arg desc 2 = The skill whose level changed. This is a constant like <code>SkillType.Combat</code>, which can be converted to the game's internal skill ID using <code>(int)e.Skill</code>.
  
  |arg name 3 = <tt>OldLevel</tt>
+
  |arg name 3 = <samp>e.OldLevel</samp>
  |arg type 3 = <tt>int</tt>
+
  |arg type 3 = <samp>int</samp>
 
  |arg desc 3 = The player's previous level for that skill.
 
  |arg desc 3 = The player's previous level for that skill.
  
  |arg name 4 = <tt>NewLevel</tt>
+
  |arg name 4 = <samp>e.NewLevel</samp>
  |arg type 4 = <tt>int</tt>
+
  |arg type 4 = <samp>int</samp>
 
  |arg desc 4 = The player's new level for that skill.
 
  |arg desc 4 = The player's new level for that skill.
  
  |arg name 5 = <tt>e.IsLocalPlayer</tt>
+
  |arg name 5 = <samp>e.IsLocalPlayer</samp>
  |arg type 5 = <tt>bool</tt>
+
  |arg type 5 = <samp>bool</samp>
 
  |arg desc 5 = Whether the affected player is the local one.
 
  |arg desc 5 = Whether the affected player is the local one.
 
}}
 
}}
Line 606: Line 609:
 
  |desc  = Raised after the current player moves to a new location.
 
  |desc  = Raised after the current player moves to a new location.
  
  |arg name 1 = <tt>e.Player</tt>
+
  |arg name 1 = <samp>e.Player</samp>
  |arg type 1 = <tt>Farmer</tt>
+
  |arg type 1 = <samp>Farmer</samp>
 
  |arg desc 1 = The player who warped to a new location.
 
  |arg desc 1 = The player who warped to a new location.
  
  |arg name 2 = <tt>OldLocation</tt>
+
  |arg name 2 = <samp>e.OldLocation</samp>
  |arg type 2 = <tt>GameLocation</tt>
+
  |arg type 2 = <samp>GameLocation</samp>
 
  |arg desc 2 = The player's previous location.
 
  |arg desc 2 = The player's previous location.
  
  |arg name 3 = <tt>NewLocation</tt>
+
  |arg name 3 = <samp>e.NewLocation</samp>
  |arg type 3 = <tt>GameLocation</tt>
+
  |arg type 3 = <samp>GameLocation</samp>
 
  |arg desc 3 = The player's new location.
 
  |arg desc 3 = The player's new location.
  
  |arg name 4 = <tt>e.IsLocalPlayer</tt>
+
  |arg name 4 = <samp>e.IsLocalPlayer</samp>
  |arg type 4 = <tt>bool</tt>
+
  |arg type 4 = <samp>bool</samp>
 
  |arg desc 4 = Whether the affected player is the local one.
 
  |arg desc 4 = Whether the affected player is the local one.
 
}}
 
}}
Line 625: Line 628:
  
 
===World===
 
===World===
<tt>this.Helper.Events.World</tt> has events raised when the in-game world changes in some way.
+
<samp>this.Helper.Events.World</samp> has events raised when the in-game world changes in some way.
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 636: Line 639:
 
  |desc  = Raised after a game location is added or removed (including building interiors).
 
  |desc  = Raised after a game location is added or removed (including building interiors).
  
  |arg name 1 = <tt>e.Added</tt>
+
  |arg name 1 = <samp>e.Added</samp>
  |arg type 1 = <tt>IEnumerable&lt;GameLocation&gt;</tt>
+
  |arg type 1 = <samp>IEnumerable&lt;GameLocation&gt;</samp>
 
  |arg desc 1 = A list of locations added since the last update tick.
 
  |arg desc 1 = A list of locations added since the last update tick.
  
  |arg name 2 = <tt>e.Removed</tt>
+
  |arg name 2 = <samp>e.Removed</samp>
  |arg type 2 = <tt>IEnumerable&lt;GameLocation&gt;</tt>
+
  |arg type 2 = <samp>IEnumerable&lt;GameLocation&gt;</samp>
 
  |arg desc 2 = A list of locations removed since the last update tick.
 
  |arg desc 2 = A list of locations removed since the last update tick.
 
}}
 
}}
 
 
{{/event
 
{{/event
 
  |group = World
 
  |group = World
Line 650: Line 652:
 
  |desc  = Raised after buildings are added/removed in any location.
 
  |desc  = Raised after buildings are added/removed in any location.
  
  |arg name 1 = <tt>e.Location</tt>
+
This event isn't raised for buildings already present when a location is added. If you need to handle those too, use [[#World.LocationListChanged|<samp>LocationListChanged</samp>]] and check <samp>e.Added.OfType<BuildableGameLocation>()</samp> → <samp>buildings</samp>.
  |arg type 1 = <tt>GameLocation</tt>
+
 
 +
  |arg name 1 = <samp>e.Location</samp>
 +
  |arg type 1 = <samp>GameLocation</samp>
 
  |arg desc 1 = The location which changed.
 
  |arg desc 1 = The location which changed.
  
  |arg name 2 = <tt>e.Added</tt>
+
  |arg name 2 = <samp>e.Added</samp>
  |arg type 2 = <tt>IEnumerable&lt;Building&gt;</tt>
+
  |arg type 2 = <samp>IEnumerable&lt;Building&gt;</samp>
 
  |arg desc 2 = A list of buildings added since the last update tick.
 
  |arg desc 2 = A list of buildings added since the last update tick.
  
  |arg name 3 = <tt>e.Removed</tt>
+
  |arg name 3 = <samp>e.Removed</samp>
  |arg type 3 = <tt>IEnumerable&lt;Building&gt;</tt>
+
  |arg type 3 = <samp>IEnumerable&lt;Building&gt;</samp>
 
  |arg desc 3 = A list of buildings removed since the last update tick.
 
  |arg desc 3 = A list of buildings removed since the last update tick.
 +
 +
|arg name 4 = <samp>e.IsCurrentLocation</samp>
 +
|arg type 4 = <samp>bool</samp>
 +
|arg desc 4 = Whether <samp>e.Location</samp> is the location containing the local player.
 +
}}
 +
{{/event
 +
|group = World
 +
|name  = ChestInventoryChanged
 +
|desc  = Raised after items are added or removed from a chest's inventory.
 +
 +
|arg name 1 = <samp>e.Chest</samp>
 +
|arg type 1 = <samp>Chest</samp>
 +
|arg desc 1 = The chest whose inventory changed.
 +
 +
|arg name 2 = <samp>e.Location</samp>
 +
|arg type 2 = <samp>Location</samp>
 +
|arg desc 2 = The location containing the chest.
 +
 +
|arg name 3 = <samp>e.Added</samp>
 +
|arg type 3 = <samp>IEnumerable&lt;Item&gt;</samp>
 +
|arg desc 3 = The added item stacks.
 +
 +
|arg name 4 = <samp>e.Removed</samp>
 +
|arg type 4 = <samp>IEnumerable&lt;Item&gt;</samp>
 +
|arg desc 4 = The removed item stacks.
 +
 +
|arg name 5 = <samp>e.QuantityChanged</samp>
 +
|arg type 5 = <samp>IEnumerable&lt;ItemStackSizeChange&gt;</samp>
 +
|arg desc 5 = The item stacks whose quantity changed. Each <samp>ItemStackSizeChange</samp> instance includes <samp>Item</samp> (the affected item stack), <samp>OldSize</samp> (the previous stack size), and <samp>NewSize</samp> (the new stack size).
 
}}
 
}}
 
{{/event
 
{{/event
Line 667: Line 700:
 
  |desc  = Raised after debris is added/removed in any location (including dropped or spawned floating items).
 
  |desc  = Raised after debris is added/removed in any location (including dropped or spawned floating items).
  
  |arg name 1 = <tt>e.Location</tt>
+
This event isn't raised for debris already present when a location is added. If you need to handle those too, use [[#World.LocationListChanged|<samp>LocationListChanged</samp>]] and check <samp>e.Added</samp> → <samp>debris</samp>.
  |arg type 1 = <tt>GameLocation</tt>
+
 
 +
  |arg name 1 = <samp>e.Location</samp>
 +
  |arg type 1 = <samp>GameLocation</samp>
 
  |arg desc 1 = The location which changed.
 
  |arg desc 1 = The location which changed.
  
  |arg name 2 = <tt>e.Added</tt>
+
  |arg name 2 = <samp>e.Added</samp>
  |arg type 2 = <tt>IEnumerable&lt;Debris&gt;</tt>
+
  |arg type 2 = <samp>IEnumerable&lt;Debris&gt;</samp>
 
  |arg desc 2 = A list of debris added since the last update tick.
 
  |arg desc 2 = A list of debris added since the last update tick.
  
  |arg name 3 = <tt>e.Removed</tt>
+
  |arg name 3 = <samp>e.Removed</samp>
  |arg type 3 = <tt>IEnumerable&lt;Debris&gt;</tt>
+
  |arg type 3 = <samp>IEnumerable&lt;Debris&gt;</samp>
 
  |arg desc 3 = A list of debris removed since the last update tick.
 
  |arg desc 3 = A list of debris removed since the last update tick.
 +
 +
|arg name 4 = <samp>e.IsCurrentLocation</samp>
 +
|arg type 4 = <samp>bool</samp>
 +
|arg desc 4 = Whether <samp>e.Location</samp> is the location containing the local player.
 +
}}
 +
{{/event
 +
|group = World
 +
|name  = FurnitureListChanged
 +
|desc  = Raised after furniture are added/removed in any location.
 +
 +
This event isn't raised for furniture already present when a location is added. If you need to handle those too, use [[#World.LocationListChanged|<samp>LocationListChanged</samp>]] and check <samp>e.Added</samp> → <samp>furniture</samp>.
 +
 +
|arg name 1 = <samp>e.Location</samp>
 +
|arg type 1 = <samp>GameLocation</samp>
 +
|arg desc 1 = The location which changed.
 +
 +
|arg name 2 = <samp>e.Added</samp>
 +
|arg type 2 = <samp>IEnumerable&lt;Furniture&gt;</samp>
 +
|arg desc 2 = A list of furniture added since the last update tick.
 +
 +
|arg name 3 = <samp>e.Removed</samp>
 +
|arg type 3 = <samp>IEnumerable&lt;Furniture&gt;</samp>
 +
|arg desc 3 = A list of furniture removed since the last update tick.
 +
 +
|arg name 4 = <samp>e.IsCurrentLocation</samp>
 +
|arg type 4 = <samp>bool</samp>
 +
|arg desc 4 = Whether <samp>e.Location</samp> is the location containing the local player.
 
}}
 
}}
 
{{/event
 
{{/event
Line 684: Line 746:
 
  |desc  = Raised after large terrain features (like bushes) are added/removed in any location.
 
  |desc  = Raised after large terrain features (like bushes) are added/removed in any location.
  
  |arg name 1 = <tt>e.Location</tt>
+
This event isn't raised for large terrain features already present when a location is added. If you need to handle those too, use [[#World.LocationListChanged|<samp>LocationListChanged</samp>]] and check <samp>e.Added</samp> → <samp>largeTerrainFeatures</samp>.
  |arg type 1 = <tt>GameLocation</tt>
+
 
 +
  |arg name 1 = <samp>e.Location</samp>
 +
  |arg type 1 = <samp>GameLocation</samp>
 
  |arg desc 1 = The location which changed.
 
  |arg desc 1 = The location which changed.
  
  |arg name 2 = <tt>e.Added</tt>
+
  |arg name 2 = <samp>e.Added</samp>
  |arg type 2 = <tt>IEnumerable&lt;LargeTerrainFeature&gt;</tt>
+
  |arg type 2 = <samp>IEnumerable&lt;LargeTerrainFeature&gt;</samp>
 
  |arg desc 2 = A list of large terrain features added since the last update tick.
 
  |arg desc 2 = A list of large terrain features added since the last update tick.
  
  |arg name 3 = <tt>e.Removed</tt>
+
  |arg name 3 = <samp>e.Removed</samp>
  |arg type 3 = <tt>IEnumerable&lt;LargeTerrainFeature&gt;</tt>
+
  |arg type 3 = <samp>IEnumerable&lt;LargeTerrainFeature&gt;</samp>
 
  |arg desc 3 = A list of large terrain features removed since the last update tick.
 
  |arg desc 3 = A list of large terrain features removed since the last update tick.
 +
 +
|arg name 4 = <samp>e.IsCurrentLocation</samp>
 +
|arg type 4 = <samp>bool</samp>
 +
|arg desc 4 = Whether <samp>e.Location</samp> is the location containing the local player.
 
}}
 
}}
 
{{/event
 
{{/event
Line 701: Line 769:
 
  |desc  = Raised after NPCs are added/removed in any location (including villagers, horses, Junimos, monsters, and pets).
 
  |desc  = Raised after NPCs are added/removed in any location (including villagers, horses, Junimos, monsters, and pets).
  
  |arg name 1 = <tt>e.Location</tt>
+
This event isn't raised for characters already present when a location is added. If you need to handle those too, use [[#World.LocationListChanged|<samp>LocationListChanged</samp>]] and check <samp>e.Added</samp> → <samp>characters</samp>.
  |arg type 1 = <tt>GameLocation</tt>
+
 
 +
  |arg name 1 = <samp>e.Location</samp>
 +
  |arg type 1 = <samp>GameLocation</samp>
 
  |arg desc 1 = The location which changed.
 
  |arg desc 1 = The location which changed.
  
  |arg name 2 = <tt>e.Added</tt>
+
  |arg name 2 = <samp>e.Added</samp>
  |arg type 2 = <tt>IEnumerable&lt;NPC&gt;</tt>
+
  |arg type 2 = <samp>IEnumerable&lt;NPC&gt;</samp>
 
  |arg desc 2 = A list of NPCs added since the last update tick.
 
  |arg desc 2 = A list of NPCs added since the last update tick.
  
  |arg name 3 = <tt>e.Removed</tt>
+
  |arg name 3 = <samp>e.Removed</samp>
  |arg type 3 = <tt>IEnumerable&lt;NPC&gt;</tt>
+
  |arg type 3 = <samp>IEnumerable&lt;NPC&gt;</samp>
 
  |arg desc 3 = A list of NPCs removed since the last update tick.
 
  |arg desc 3 = A list of NPCs removed since the last update tick.
 +
 +
|arg name 4 = <samp>e.IsCurrentLocation</samp>
 +
|arg type 4 = <samp>bool</samp>
 +
|arg desc 4 = Whether <samp>e.Location</samp> is the location containing the local player.
 
}}
 
}}
 
{{/event
 
{{/event
 
  |group = World
 
  |group = World
 
  |name  = ObjectListChanged
 
  |name  = ObjectListChanged
  |desc  = Raised after objects are added/removed in any location (including machines, furniture, fences, etc). For floating items, see <tt>DebrisListChanged</tt>.
+
  |desc  = Raised after objects are added/removed in any location (including machines, fences, etc). This doesn't apply for floating items (see <samp>DebrisListChanged</samp>) or furniture (see <samp>FurnitureListChanged</samp>).
 +
 
 +
This event isn't raised for objects already present when a location is added. If you need to handle those too, use [[#World.LocationListChanged|<samp>LocationListChanged</samp>]] and check <samp>e.Added</samp> → <samp>objects</samp>.
  
  |arg name 1 = <tt>e.Location</tt>
+
  |arg name 1 = <samp>e.Location</samp>
  |arg type 1 = <tt>GameLocation</tt>
+
  |arg type 1 = <samp>GameLocation</samp>
 
  |arg desc 1 = The location which changed.
 
  |arg desc 1 = The location which changed.
  
  |arg name 2 = <tt>e.Added</tt>
+
  |arg name 2 = <samp>e.Added</samp>
  |arg type 2 = <tt>IEnumerable&lt;KeyValuePair&lt;Vector2, Object&gt;&gt;</tt>
+
  |arg type 2 = <samp>IEnumerable&lt;KeyValuePair&lt;Vector2, Object&gt;&gt;</samp>
 
  |arg desc 2 = A list of [[Modding:Modder Guide/Game Fundamentals#Tiles|tile coordinate]] + object pairs added since the last update tick.
 
  |arg desc 2 = A list of [[Modding:Modder Guide/Game Fundamentals#Tiles|tile coordinate]] + object pairs added since the last update tick.
  
  |arg name 3 = <tt>e.Removed</tt>
+
  |arg name 3 = <samp>e.Removed</samp>
  |arg type 3 = <tt>IEnumerable&lt;KeyValuePair&lt;Vector2, Object&gt;&gt;</tt>
+
  |arg type 3 = <samp>IEnumerable&lt;KeyValuePair&lt;Vector2, Object&gt;&gt;</samp>
 
  |arg desc 3 = A list of [[Modding:Modder Guide/Game Fundamentals#Tiles|tile coordinate]] + object pairs removed since the last update tick.
 
  |arg desc 3 = A list of [[Modding:Modder Guide/Game Fundamentals#Tiles|tile coordinate]] + object pairs removed since the last update tick.
 +
 +
|arg name 4 = <samp>e.IsCurrentLocation</samp>
 +
|arg type 4 = <samp>bool</samp>
 +
|arg desc 4 = Whether <samp>e.Location</samp> is the location containing the local player.
 
}}
 
}}
 
{{/event
 
{{/event
 
  |group = World
 
  |group = World
 
  |name  = TerrainFeatureListChanged
 
  |name  = TerrainFeatureListChanged
  |desc  = Raised after terrain features are added/removed in any location (including trees, hoed dirt, and flooring). For bushes, see <tt>LargeTerrainFeatureListChanged</tt>.
+
  |desc  = Raised after terrain features are added/removed in any location (including trees, hoed dirt, and flooring). For bushes, see <samp>LargeTerrainFeatureListChanged</samp>.
  
  |arg name 1 = <tt>e.Location</tt>
+
This event isn't raised for terrain features already present when a location is added. If you need to handle those too, use [[#World.LocationListChanged|<samp>LocationListChanged</samp>]] and check <samp>e.Added</samp> → <samp>terrainFeatures</samp>.
  |arg type 1 = <tt>GameLocation</tt>
+
 
 +
  |arg name 1 = <samp>e.Location</samp>
 +
  |arg type 1 = <samp>GameLocation</samp>
 
  |arg desc 1 = The location which changed.
 
  |arg desc 1 = The location which changed.
  
  |arg name 2 = <tt>e.Added</tt>
+
  |arg name 2 = <samp>e.Added</samp>
  |arg type 2 = <tt>IEnumerable&lt;KeyValuePair&lt;Vector2, TerrainFeature&gt;&gt;</tt>
+
  |arg type 2 = <samp>IEnumerable&lt;KeyValuePair&lt;Vector2, TerrainFeature&gt;&gt;</samp>
 
  |arg desc 2 = A list of [[Modding:Modder Guide/Game Fundamentals#Tiles|tile coordinate]] + terrain feature pairs added since the last update tick.
 
  |arg desc 2 = A list of [[Modding:Modder Guide/Game Fundamentals#Tiles|tile coordinate]] + terrain feature pairs added since the last update tick.
  
  |arg name 3 = <tt>e.Removed</tt>
+
  |arg name 3 = <samp>e.Removed</samp>
  |arg type 3 = <tt>IEnumerable&lt;KeyValuePair&lt;Vector2, TerrainFeature&gt;&gt;</tt>
+
  |arg type 3 = <samp>IEnumerable&lt;KeyValuePair&lt;Vector2, TerrainFeature&gt;&gt;</samp>
 
  |arg desc 3 = A list of [[Modding:Modder Guide/Game Fundamentals#Tiles|tile coordinate]] + terrain feature pairs removed since the last update tick.
 
  |arg desc 3 = A list of [[Modding:Modder Guide/Game Fundamentals#Tiles|tile coordinate]] + terrain feature pairs removed since the last update tick.
 +
 +
|arg name 4 = <samp>e.IsCurrentLocation</samp>
 +
|arg type 4 = <samp>bool</samp>
 +
|arg desc 4 = Whether <samp>e.Location</samp> is the location containing the local player.
 
}}
 
}}
 
|}
 
|}
  
 
===Specialised===
 
===Specialised===
<tt>this.Helper.Events.Specialised</tt> has events for specialised edge cases. These shouldn't be used by most mods.
+
<samp>this.Helper.Events.Specialised</samp> has events for specialised edge cases. These shouldn't be used by most mods.
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 756: Line 842:
 
! event
 
! event
 
! summary
 
! summary
 +
{{/event
 +
|group = Specialised
 +
|name  = LoadStageChanged
 +
|desc  = Raised when the low-level stage in the game's loading process has changed, for mods which need to run code at specific points in the loading process. The available stages or when they happen might change without warning in future versions (''e.g.,'' due to changes in the game's load process), so '''mods using this event are more likely to break or have bugs'''. Most mods should use the [[#Game loop|game loop events]] instead.
 +
 +
|arg name 1 = <samp>e.NewStage</samp>
 +
|arg type 1 = <samp>LoadStage</samp>
 +
|arg desc 1 = The new load stage. The possible values are...
 +
 +
* <code>None</code>: a save is not loaded or loading. (For example, the player is on the title screen.)
 +
* When creating a new save slot:
 +
** <code>CreatedBasicInfo</code>: the game has initialised the basic save info.
 +
** <code>CreatedInitialLocations</code>: the game has added the location instances, but hasn't fully initialized them yet.
 +
** <code>CreatedLocations</code>: the game has initialised the in-game locations.
 +
** <code>CreatedSaveFile</code>: the game has created the physical save files.
 +
* When loading an existing save slot:
 +
** <code>SaveParsed</code>: the game has read the raw save data into <samp>StardewValley.SaveGame.loaded</samp>. Not applicable when connecting to a multiplayer host. This is equivalent to <samp>StardewValley.SaveGame.getLoadEnumerator</samp> value 20.
 +
** <code>SaveLoadedBasicInfo</code>: the game has applied the basic save info (including player data). Not applicable when connecting to a multiplayer host. Note that some basic info (like daily luck) is not initialised at this point. This is equivalent to <samp>StardewValley.SaveGame.getLoadEnumerator</samp> value 36.
 +
** <code>SaveAddedLocations</code>: the game has added the location instances to the game, but hasn't restored their save data yet.
 +
** <code>SaveLoadedLocations</code>: the game has restored the in-game location data. Not applicable when connecting to a multiplayer host. This is equivalent to <samp>StardewValley.SaveGame.getLoadEnumerator</samp> value 50.
 +
* <code>Preloaded</code>: the final metadata has been loaded from the save file. This happens before the game applies problem fixes, checks for achievements, starts music, etc. Not applicable when connecting to a multiplayer host.
 +
* <code>Loaded</code>: the save is fully loaded, but the world may not be fully initialised yet.
 +
* <code>Ready</code>: the save is fully loaded, the world has been initialised, and [[Modding:Modder Guide/APIs/Utilities#Context|<samp>Context.IsWorldReady</samp>]] is now true.
 +
* <code>ReturningToTitle</code>: The game is exiting the loaded save and returning to the title screen. This happens before it returns to title; the stage ''after'' it returns to title is <code>None</code>.
 +
 +
|arg name 2 = <samp>e.OldStage</samp>
 +
|arg type 2 = <samp>LoadStage</samp>
 +
|arg desc 2 = The previous load stage. See comments for <samp>e.NewStage</samp>.
 +
}}
 
{{/event
 
{{/event
 
  |group = Specialised
 
  |group = Specialised
Line 762: Line 877:
  
  
  |arg name 1 = <tt>e.Ticks</tt>
+
  |arg name 1 = <samp>e.Ticks</samp>
  |arg type 1 = <tt>int</tt>
+
  |arg type 1 = <samp>int</samp>
 
  |arg desc 1 = The number of ticks elapsed since the game started, including the current tick.
 
  |arg desc 1 = The number of ticks elapsed since the game started, including the current tick.
  
  |arg name 2 = <tt>e.IsOneSecond</tt>
+
  |arg name 2 = <samp>e.IsOneSecond</samp>
  |arg type 2 = <tt>bool</tt>
+
  |arg type 2 = <samp>bool</samp>
  |arg desc 2 = Whether <tt>e.TicksElapsed</tt> is a multiple of 60, which happens approximately once per second.
+
  |arg desc 2 = Whether <samp>e.TicksElapsed</samp> is a multiple of 60, which happens approximately once per second.
  
  |arg name 3 = <tt>e.IsMultipleOf(int number)</tt>
+
  |arg name 3 = <samp>e.IsMultipleOf(int number)</samp>
  |arg type 3 = ''method'' returns <tt>bool</tt>
+
  |arg type 3 = ''method'' returns <samp>bool</samp>
  |arg desc 3 = Whether <tt>e.TicksElapsed</tt> is a multiple of the given number. This is mainly useful if you want to run logic intermittently (e.g. <code>e.IsMultipleOf(30)</code> for every half-second).
+
  |arg desc 3 = Whether <samp>e.TicksElapsed</samp> is a multiple of the given number. This is mainly useful if you want to run logic intermittently (''e.g.,'' <code>e.IsMultipleOf(30)</code> for every half-second).
 
}}
 
}}
 
|}
 
|}
  
{{modding guide footer
+
==Advanced==
|prev = [[Modding:Modder Guide/APIs|SMAPI reference]]
+
===Change monitoring===
|next =
+
You may want to handle a change that doesn't have its own event (''e.g.,'' an NPC's heart event ends, a letter is added to the mailbox, etc). You can usually do that by handling a general event like [[#GameLoop.UpdateTicked|<samp>UpdateTicked</samp>]], and detecting when the value(s) you're watching changed. For example, here's a complete mod which logs a message when an in-game event ends:
}}
+
<syntaxhighlight lang="c#">
 +
/// <summary>The main entry point for the mod.</summary>
 +
internal sealed class ModEntry : Mod
 +
{
 +
    /*********
 +
    ** Fields
 +
    *********/
 +
    /// <summary>The in-game event detected on the last update tick.</summary>
 +
    private Event LastEvent;
 +
 
 +
 
 +
    /*********
 +
    ** Public methods
 +
    *********/
 +
    /// <summary>The mod entry point, called after the mod is first loaded.</summary>
 +
    /// <param name="helper">Provides simplified APIs for writing mods.</param>
 +
    public override void Entry(IModHelper helper)
 +
    {
 +
        helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked;
 +
    }
 +
 
 +
 
 +
    /*********
 +
    ** Private methods
 +
    *********/
 +
    /// <summary>The method invoked when the game updates its state.</summary>
 +
    /// <param name="sender">The event sender.</param>
 +
    /// <param name="e">The event arguments.</param>
 +
    private void OnUpdateTicked(object sender, EventArgs e)
 +
    {
 +
        if (this.LastEvent != null && Game1.CurrentEvent == null)
 +
            this.Monitor.Log($"Event {this.LastEvent.id} just ended!");
 +
 
 +
        this.LastEvent = Game1.CurrentEvent;
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 
 +
===Custom priority===
 +
SMAPI calls event handlers in the same order they're registered by default, so the first event handler registered is the first to receive the event each time. This isn't always predictable, since it depends on mod load order and when each mod registers their handlers. This order is also an implementation detail, so it's not guaranteed.
 +
 
 +
If you need more control over the order, you can specify an event priority using the <code>[EventPriority]</code> attribute: <samp>Low</samp> (after most handlers), <samp>Default</samp>, <samp>High</samp> (before most handlers), or a custom value (''e.g.,'' <samp>High + 1</samp> is higher priority than <samp>High</samp>). '''You should only do this if strictly needed'''; depending on event handler order between mods is fragile (''e.g.,'' the other mod might change its priority too).
 +
 
 +
<syntaxhighlight lang="c#">
 +
/// <summary>The main entry point for the mod.</summary>
 +
internal sealed class ModEntry : Mod
 +
{
 +
    /*********
 +
    ** Public methods
 +
    *********/
 +
    /// <summary>The mod entry point, called after the mod is first loaded.</summary>
 +
    /// <param name="helper">Provides simplified APIs for writing mods.</param>
 +
    public override void Entry(IModHelper helper)
 +
    {
 +
        helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked;
 +
    }
 +
 
 +
 
 +
    /*********
 +
    ** Private methods
 +
    *********/
 +
    /// <summary>The method invoked when the game updates its state.</summary>
 +
    /// <param name="sender">The event sender.</param>
 +
    /// <param name="e">The event arguments.</param>
 +
    [EventPriority(EventPriority.High)]
 +
    private void OnUpdateTicked(object sender, EventArgs e)
 +
    {
 +
        this.Monitor.Log("Update!");
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 
 +
[[zh:模组:制作指南/APIs/Events]]

Latest revision as of 18:05, 6 April 2024

Creating SMAPI mods SMAPI mascot.png


Modding:Index

SMAPI provides several C# events which let your mod respond when something happens (like when the player places an object) or run code periodically (like once per update tick).

FAQs

What are events?

Events let you run code when something happens. You can add any number of event handlers (methods to call) when an event (what happened) is raised. You can think of events and handlers as a when..then statement:

WHEN the save is loaded,   <-- event
THEN run my code           <-- event handler

See Events in the C# programming guide for more info.

How do I use them?

Event handlers are usually added in your Entry method, though you can add and remove them anytime. For example, let's say you want to print a message when each day starts. First you would choose the appropriate event from the list below (GameLoop.DayStarted), then add an event handler, and do something in your method code:

/// <summary>The main entry point for the mod.</summary>
internal sealed class ModEntry : Mod
{
    /**********
    ** Public methods
    *********/
    /// <summary>The mod entry point, called after the mod is first loaded.</summary>
    /// <param name="helper">Provides simplified APIs for writing mods.</param>
    public override void Entry(IModHelper helper)
    {
        // event += method to call
        helper.Events.GameLoop.DayStarted += this.OnDayStarted;
    }

    /// <summary>The method called after a new day starts.</summary>
    /// <param name="sender">The event sender.</param>
    /// <param name="e">The event arguments.</param>
    private void OnDayStarted(object? sender, DayStartedEventArgs e)
    {
       this.Monitor.Log("A new day dawns!", LogLevel.Info);
    }
}

Tip: you don't need to memorise the method arguments. In Visual Studio, type helper.Events.GameLoop.SaveLoaded += and press TAB to auto-create a method. (There doesn't seem to be an equivalent feature in MonoDevelop.)

sender is part of the C# event pattern, and is unused by SMAPI.

How do events fit into the game?

Events are raised on each game tick (when the game updates its state and renders to the screen), which is 60 times per second. An event may be raised multiple times (e.g., if the player pressed two keys simultaneously), but most events won't be raised 60 times per second (e.g., players are unlikely to be pressing 60 buttons per second).

Your event handlers are run synchronously: the game is paused and no other mod's code will run simultaneously, so there's no risk of conflicting changes. Since code runs very quickly, players won't notice any delay unless your code is unusually slow. That said, when using frequent events like UpdateTicked or Rendered, you should cache expensive operations (like loading an asset) instead of repeating them in each tick to avoid impacting performance.

What if a mod changes what the event was raised for?

Events are raised based on a snapshot of the game state. That's usually but not necessarily the current game state.

For example, consider this case:

  1. The GameMenu opens.
  2. SMAPI raises the MenuChanged event, which mods A and B listen to.
  3. Mod A receives the event and closes the menu.
  4. Mod B receives the event.

Each mod is still handling the MenuChanged event for the opened menu, even though the first mod closed it. SMAPI will raise a new MenuChanged event for the closed menu on the next tick.

This rarely affects mods, but it's something to keep in mind if you need the current state (e.g., check Game1.activeClickableMenu instead of e.NewMenu).

Events

The available events are documented below.

Content

this.Helper.Events.Content has events related to assets loaded from the content pipeline.

event summary
#AssetRequested Raised when an asset is being requested from the content pipeline. The asset isn't necessarily being loaded yet (e.g. the game may be checking if it exists).

You can register the changes you want to apply using the event arguments below; they'll be applied when the asset is actually loaded. See the content API for more info.

If the asset is requested multiple times in the same tick (e.g. once to check if it exists and once to load it), SMAPI might only raise the event once and reuse the cached result.

Event arguments:

event arg type description
e.Name IAssetName The name of the asset being requested, including the locale code if any (like the .fr-FR in Data/Bundles.fr-FR). This includes utility methods to parse the value, like e.Name.IsEquivalentTo("Portraits/Abigail") (which handles any differences in formatting for you).
e.NameWithoutLocale IAssetName Equivalent to e.Name but without the locale code (if any). For example, if e.Name is Data/Bundles.fr-FR, this field will contain Data/Bundles.
e.LoadFrom(...) method Call this method to provide the initial instance for the asset, instead of trying to load it from the game's Content folder. For example:
e.LoadFrom(() => this.Helper.Content.Load<Texture2D>("assets/portraits.png"), AssetLoadPriority.Medium);

Usage notes:

  • The asset doesn't need to exist in the game's Content folder. If any mod loads the asset, the game will see it as an existing asset as if it was in that folder.
  • Each asset can logically only have one initial instance. If multiple loads apply at the same time, SMAPI will raise an error and ignore all of them. If you're making changes to the existing asset instead of replacing it, you should use the Edit method instead to avoid those limitations and improve mod compatibility.
e.LoadFromModFile<T>(...) method Call this method to provide the initial instance for the asset by loading a file from your mod folder. For example:
e.LoadFromModFile<Texture2D>("assets/portraits.png", AssetLoadPriority.Medium);
See usage notes on e.LoadFrom.
e.Edit(...) method Call this method to apply edits to the asset after it's loaded. For example:
e.Edit(asset =>
{
    Texture2D ribbon = this.Helper.Content.Load<Texture2D>("assets/ribbon.png");
    asset.AsImage().PatchImage(source: overlay, patchMode: PatchMode.Overlay);
});

Usage notes:

  • Editing an asset which doesn't exist has no effect. This is applied after the asset is loaded from the game's Content folder, or from any mod's LoadFrom or LoadFromModFile.
  • You can apply any number of edits to the asset. Each edit will be applied on top of the previous one (i.e. it'll see the merged asset from all previous edits as its input).
#AssetsInvalidated Raised after one or more assets were invalidated from the content cache by a mod, so they'll be reloaded next time they're requested. If the assets will be reloaded or propagated automatically, this event is raised before that happens.

Next time the asset is loaded (including for asset propagation), the AssetRequested event will be raised again.

Event arguments:

event arg type description
e.Names IReadOnlySet<IAssetName> The asset names that were invalidated, including the locale code if any (like the .fr-FR in Data/Bundles.fr-FR). These include utility methods to parse the values, like name.IsEquivalentTo("Portraits/Abigail") (which handles any differences in formatting for you).
e.NamesWithoutLocale IReadOnlySet<IAssetName> Equivalent to e.Names but without the locale code (if any). For example, if e.Names contains Data/Bundles.fr-FR, this field will contain Data/Bundles.
#AssetReady Raised after an asset is loaded by the content pipeline, after any mod edits specified via AssetRequested have been applied.

This event is only raised if something requested the asset from the content pipeline. Invalidating an asset from the content cache won't necessarily reload it automatically.

Event arguments:

event arg type description
e.Name IAssetName The name of the asset that was loaded, including the locale code if any (like the .fr-FR in Data/Bundles.fr-FR). This includes utility methods to parse the value, like e.Name.IsEquivalentTo("Portraits/Abigail") (which handles any differences in formatting for you).
e.NameWithoutLocale IAssetName Equivalent to e.Name but without the locale code (if any). For example, if e.Name is Data/Bundles.fr-FR, this field will contain Data/Bundles.
#LocaleChanged Raised after the game language changes. For non-English players, this may be raised during startup when the game switches to the previously selected language.

Event arguments:

event arg type description
e.OldLanguage LanguageCode The previous value for the game's language enum. For a custom language, this is always LanguageCode.mod.
e.OldLocale string The previous value for the locale code. This is the locale code as it appears in asset names, like fr-FR in Maps/springobjects.fr-FR. The locale code for English is an empty string.
e.NewLanguage LanguageCode The new value for the game's language enum. See notes on OldLanguage.
e.NewLocale string The previous value for the locale code. See notes on OldLocale.

Display

this.Helper.Events.Display has events linked to UI and drawing to the screen.

event summary
#MenuChanged Raised after a game menu is opened, closed, or replaced.

Event arguments:

event arg type description
e.NewMenu IClickableMenu The new menu instance (or null if none).
e.OldMenu IClickableMenu The old menu instance (or null if none).
#Rendering Raised before the game draws anything to the screen in a draw tick, as soon as the sprite batch is opened. The sprite batch may be closed and reopened multiple times after this event is called, but it's only raised once per draw tick. This event isn't useful for drawing to the screen, since the game will draw over it.

Event arguments:

event arg type description
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#Rendered Raised after the game draws to the sprite batch in a draw tick, just before the final sprite batch is rendered to the screen. Since the game may open/close the sprite batch multiple times in a draw tick, the sprite batch may not contain everything being drawn and some things may already be rendered to the screen. Content drawn to the sprite batch at this point will be drawn over all vanilla content (including menus, HUD, and cursor).

Event arguments:

event arg type description
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderingWorld Raised before the game world is drawn to the screen. This event isn't useful for drawing to the screen, since the game will draw over it.

Event arguments:

event arg type description
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderedWorld Raised after the game world is drawn to the sprite batch, before it's rendered to the screen. Content drawn to the sprite batch at this point will be drawn over the world, but under any active menu, HUD elements, or cursor.

Event arguments:

event arg type description
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderingActiveMenu When a menu is open (Game1.activeClickableMenu != null), raised before that menu is drawn to the screen. This includes the game's internal menus like the title screen. Content drawn to the sprite batch at this point will appear under the menu.

Event arguments:

event arg type description
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderedActiveMenu When a menu is open (Game1.activeClickableMenu != null), raised after that menu is drawn to the sprite batch but before it's rendered to the screen. Content drawn to the sprite batch at this point will appear over the menu and menu cursor.

Event arguments:

event arg type description
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderingHud Raised before drawing the HUD (item toolbar, clock, etc) to the screen. The vanilla HUD may be hidden at this point (e.g., because a menu is open). Content drawn to the sprite batch at this point will appear under the HUD.

Event arguments:

event arg type description
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderedHud Raised after drawing the HUD (item toolbar, clock, etc) to the sprite batch, but before it's rendered to the screen. The vanilla HUD may be hidden at this point (e.g., because a menu is open). Content drawn to the sprite batch at this point will appear over the HUD.

Event arguments:

event arg type description
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#WindowResized Raised after the game window is resized.

Event arguments:

event arg type description
e.OldSize Point The previous window width (e.OldSize.X) and height (e.OldSize.Y).
e.NewSize Point The new window width (e.NewSize.X) and height (e.NewSize.Y).
#RenderingStep (Specialized) Raised before the game draws a specific step in the rendering cycle. This gives more granular control over render logic, but is more vulnerable to changes in game updates. Consider using one of the other render events if possible.

Event arguments:

event arg type description
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
e.Step RenderSteps The step being rendered in the draw cycle.
#RenderedStep (Specialized) Raised after the game draws a specific step in the rendering cycle. This gives more granular control over render logic, but is more vulnerable to changes in game updates. Consider using one of the other render events if possible.

Event arguments:

event arg type description
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
e.Step RenderSteps The step being rendered in the draw cycle.

Game loop

this.Helper.Events.GameLoop has events linked to the game's update loop. The update loop runs roughly ≈60 times/second to run game logic like state changes, action handling, etc. These are often useful, but you should consider semantic events like Input where applicable.

event summary
#GameLaunched Raised after the game is launched, right before the first update tick. This happens once per game session (unrelated to loading saves). All mods are loaded and initialised at this point, so this is a good time to set up mod integrations.
#UpdateTicking
UpdateTicked
Raised before/after the game state is updated (≈60 times per second).

Event arguments:

event arg type description
e.Ticks int The number of ticks elapsed since the game started, including the current tick.
e.IsOneSecond bool Whether e.TicksElapsed is a multiple of 60, which happens approximately once per second.
e.IsMultipleOf(int number) method returns bool Whether e.TicksElapsed is a multiple of the given number. This is mainly useful if you want to run logic intermittently (e.g., e.IsMultipleOf(30) for every half-second).
#OneSecondUpdateTicking
OneSecondUpdateTicked
Raised before/after the game state is updated, once per second.

Event arguments:

event arg type description
e.Ticks int The number of ticks elapsed since the game started, including the current tick.
e.IsMultipleOf(int number) method returns bool Whether e.TicksElapsed is a multiple of the given number. This is mainly useful if you want to run logic intermittently (e.g., e.IsMultipleOf(120) for every two seconds).
#SaveCreating
SaveCreated
Raised before/after the game creates the save file (after the new-game intro). The save won't be written until all mods have finished handling this event. This is a somewhat specialised event, since the world isn't fully initialised at this point; in most cases you should use DayStarted, Saving, or Saved instead.
#Saving
Saved
Raised before/after the game writes data to save file (except the initial save creation). The save won't be written until all mods have finished handling this event. This is also raised for farmhands in multiplayer.
#SaveLoaded Raised after loading a save (including the first day after creating a new save), or connecting to a multiplayer world. This happens right before DayStarted; at this point the save file is read and Context.IsWorldReady is true.

This event isn't raised after saving; if you want to do something at the start of each day, see DayStarted instead.

#DayStarted Raised after a new in-game day starts, or after connecting to a multiplayer world. Everything has already been initialised at this point. (To run code before the game sets up the day, see DayEnding instead.)
#DayEnding Raised before the game ends the current day. This happens before it starts setting up the next day and before Saving.
#TimeChanged Raised after the in-game clock time changes, which happens in intervals of ten in-game minutes.

Event arguments:

event arg type description
e.OldTime int The previous time of day in 24-hour notation (like 1600 for 4pm). The clock time resets when the player sleeps, so 2am (before sleeping) is 2600.
e.NewTime int The current time of day in 24-hour notation (like 1600 for 4pm). The clock time resets when the player sleeps, so 2am (before sleeping) is 2600.
#ReturnedToTitle Raised after the game returns to the title screen.

Input

this.Helper.Events.Input has events raised when the player uses a controller, keyboard, or mouse in some way. They can be used with the input API to access more info or suppress input.

event summary
#ButtonsChanged Raised after the player pressed/released any buttons on the keyboard, mouse, or controller. This includes mouse clicks. If the player pressed/released multiple keys at once, this is only raised once.

Event arguments:

event arg type description
e.Pressed SButton[] The buttons that were pressed since the previous tick.
e.Held SButton[] The buttons that were held since the previous tick.
e.Released SButton[] The buttons that were released since the previous tick.
e.Cursor ICursorPosition The cursor position and grab tile. Note: mods won't receive input sent to the chatbox.
#ButtonPressed
ButtonReleased
Raised after the player pressed/released a keyboard, mouse, or controller button. This includes mouse clicks. If the player pressed/released multiple keys at once, this is raised for each button pressed.

Event arguments:

event arg type description
e.Button SButton The button pressed or released.
e.Cursor ICursorPosition The cursor position and grab tile.
e.IsDown method returns bool Indicates whether a given button is currently pressed.
e.IsSuppressed method returns bool A method which indicates whether a given button was suppressed by a mod, so the game itself won't see it. Note: mods won't receive input sent to the chatbox.
#CursorMoved Raised after the player moves the in-game cursor.

Event arguments:

event arg type description
e.OldPosition ICursorPosition The previous cursor position and grab tile.
e.NewPosition ICursorPosition The current cursor position and grab tile.
#MouseWheelScrolled Raised after the player scrolls the mouse wheel.

Event arguments:

event arg type description
e.Position ICursorPosition The current cursor position and grab tile.
e.Delta int The amount by which the mouse wheel was scrolled since the last update.
e.OldValue
e.NewValue
int The previous and current scroll value, cumulative since the game started. Mods should generally use e.Delta instead.

Multiplayer

this.Helper.Events.Multiplayer has events raised for multiplayer messages and connections.

event summary
#PeerContextReceived Raised after the mod context for a player is received. The event is raised for any player (whether host or farmhand), including when the connecting player doesn't have SMAPI installed. This is the earliest point where messages can be sent to the player via SMAPI.

This happens immediately before the game approves the connection, so the player doesn't exist in the game yet. When connecting to the host, contextual fields like Game1.IsMasterGame or Context.IsMultiplayer may not be set yet; you can check e.Peer.IsHost to know whether the current player is a farmhand, since the host context will always be received first. Assuming another mod doesn't block the connection, the connection will be approved on the next tick.

Event arguments:

event arg type description
e.Peer IMultiplayerPeer The peer whose context was received (see Multiplayer#Get connected player info).
#PeerConnected Raised after a connection from another player is approved by the game. The event is raised for any player (whether host or farmhand), including when the connecting player doesn't have SMAPI installed. This happens after PeerContextReceived.

The player is connected to the game at this point, so methods like Game1.server.kick will work.

Event arguments:

event arg type description
e.Peer IMultiplayerPeer The peer who connected (see Multiplayer#Get connected player info).
#ModMessageReceived Raised after a mod message is received over the network.

Event arguments:

event arg type description
e.FromPlayerID long The unique ID of the player from whose computer the message was sent.
e.FromModID string The unique ID of the mod which sent the message.
e.Type string A message type which you can use to decide whether it's the one you want to handle. This isn't necessarily globally unique, so you should also check the FromModID field.
e.ReadAs<TModel>() method returns TModel Read the underlying message data into the given model type (like e.ReadAs<MyMessageClass>() or e.ReadAs<string>()). This will return a new instance each time.
#PeerDisconnected Raised after the connection to a player is severed.

Event arguments:

event arg type description
e.Peer IMultiplayerPeer The peer whose connection was severed (see Multiplayer#Get connected player info).

Player

this.Helper.Events.Player has events raised when the player data changes.

Currently these events are only raised for the current player. That will likely change in a future version, so make sure to check e.IsLocalPlayer if you only want to handle the current player.

event summary
#InventoryChanged Raised after items are added or removed from the player inventory.

Event arguments:

event arg type description
e.Player Farmer The player whose inventory changed.
e.Added IEnumerable<Item> The added item stacks.
e.Removed IEnumerable<Item> The removed item stacks.
e.QuantityChanged IEnumerable<ItemStackSizeChange> The item stacks whose quantity changed. Each ItemStackSizeChange instance includes Item (the affected item stack), OldSize (the previous stack size), and NewSize (the new stack size).
e.IsLocalPlayer bool Whether the affected player is the local one.
#LevelChanged Raised after a player's skill level changes. When the player levels up normally, this is raised immediately (not when the game notifies the player after they go to bed).

Event arguments:

event arg type description
e.Player Farmer The player whose skill level changed.
e.Skill SkillType The skill whose level changed. This is a constant like SkillType.Combat, which can be converted to the game's internal skill ID using (int)e.Skill.
e.OldLevel int The player's previous level for that skill.
e.NewLevel int The player's new level for that skill.
e.IsLocalPlayer bool Whether the affected player is the local one.
#Warped Raised after the current player moves to a new location.

Event arguments:

event arg type description
e.Player Farmer The player who warped to a new location.
e.OldLocation GameLocation The player's previous location.
e.NewLocation GameLocation The player's new location.
e.IsLocalPlayer bool Whether the affected player is the local one.

World

this.Helper.Events.World has events raised when the in-game world changes in some way.

event summary
#LocationListChanged Raised after a game location is added or removed (including building interiors).

Event arguments:

event arg type description
e.Added IEnumerable<GameLocation> A list of locations added since the last update tick.
e.Removed IEnumerable<GameLocation> A list of locations removed since the last update tick.
#BuildingListChanged Raised after buildings are added/removed in any location.

This event isn't raised for buildings already present when a location is added. If you need to handle those too, use LocationListChanged and check e.Added.OfType<BuildableGameLocation>()buildings.

Event arguments:

event arg type description
e.Location GameLocation The location which changed.
e.Added IEnumerable<Building> A list of buildings added since the last update tick.
e.Removed IEnumerable<Building> A list of buildings removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#ChestInventoryChanged Raised after items are added or removed from a chest's inventory.

Event arguments:

event arg type description
e.Chest Chest The chest whose inventory changed.
e.Location Location The location containing the chest.
e.Added IEnumerable<Item> The added item stacks.
e.Removed IEnumerable<Item> The removed item stacks.
e.QuantityChanged IEnumerable<ItemStackSizeChange> The item stacks whose quantity changed. Each ItemStackSizeChange instance includes Item (the affected item stack), OldSize (the previous stack size), and NewSize (the new stack size).
#DebrisListChanged Raised after debris is added/removed in any location (including dropped or spawned floating items).

This event isn't raised for debris already present when a location is added. If you need to handle those too, use LocationListChanged and check e.Addeddebris.

Event arguments:

event arg type description
e.Location GameLocation The location which changed.
e.Added IEnumerable<Debris> A list of debris added since the last update tick.
e.Removed IEnumerable<Debris> A list of debris removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#FurnitureListChanged Raised after furniture are added/removed in any location.

This event isn't raised for furniture already present when a location is added. If you need to handle those too, use LocationListChanged and check e.Addedfurniture.

Event arguments:

event arg type description
e.Location GameLocation The location which changed.
e.Added IEnumerable<Furniture> A list of furniture added since the last update tick.
e.Removed IEnumerable<Furniture> A list of furniture removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#LargeTerrainFeatureListChanged Raised after large terrain features (like bushes) are added/removed in any location.

This event isn't raised for large terrain features already present when a location is added. If you need to handle those too, use LocationListChanged and check e.AddedlargeTerrainFeatures.

Event arguments:

event arg type description
e.Location GameLocation The location which changed.
e.Added IEnumerable<LargeTerrainFeature> A list of large terrain features added since the last update tick.
e.Removed IEnumerable<LargeTerrainFeature> A list of large terrain features removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#NpcListChanged Raised after NPCs are added/removed in any location (including villagers, horses, Junimos, monsters, and pets).

This event isn't raised for characters already present when a location is added. If you need to handle those too, use LocationListChanged and check e.Addedcharacters.

Event arguments:

event arg type description
e.Location GameLocation The location which changed.
e.Added IEnumerable<NPC> A list of NPCs added since the last update tick.
e.Removed IEnumerable<NPC> A list of NPCs removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#ObjectListChanged Raised after objects are added/removed in any location (including machines, fences, etc). This doesn't apply for floating items (see DebrisListChanged) or furniture (see FurnitureListChanged).

This event isn't raised for objects already present when a location is added. If you need to handle those too, use LocationListChanged and check e.Addedobjects.

Event arguments:

event arg type description
e.Location GameLocation The location which changed.
e.Added IEnumerable<KeyValuePair<Vector2, Object>> A list of tile coordinate + object pairs added since the last update tick.
e.Removed IEnumerable<KeyValuePair<Vector2, Object>> A list of tile coordinate + object pairs removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#TerrainFeatureListChanged Raised after terrain features are added/removed in any location (including trees, hoed dirt, and flooring). For bushes, see LargeTerrainFeatureListChanged.

This event isn't raised for terrain features already present when a location is added. If you need to handle those too, use LocationListChanged and check e.AddedterrainFeatures.

Event arguments:

event arg type description
e.Location GameLocation The location which changed.
e.Added IEnumerable<KeyValuePair<Vector2, TerrainFeature>> A list of tile coordinate + terrain feature pairs added since the last update tick.
e.Removed IEnumerable<KeyValuePair<Vector2, TerrainFeature>> A list of tile coordinate + terrain feature pairs removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.

Specialised

this.Helper.Events.Specialised has events for specialised edge cases. These shouldn't be used by most mods.

event summary
#LoadStageChanged Raised when the low-level stage in the game's loading process has changed, for mods which need to run code at specific points in the loading process. The available stages or when they happen might change without warning in future versions (e.g., due to changes in the game's load process), so mods using this event are more likely to break or have bugs. Most mods should use the game loop events instead.

Event arguments:

event arg type description
e.NewStage LoadStage The new load stage. The possible values are...
  • None: a save is not loaded or loading. (For example, the player is on the title screen.)
  • When creating a new save slot:
    • CreatedBasicInfo: the game has initialised the basic save info.
    • CreatedInitialLocations: the game has added the location instances, but hasn't fully initialized them yet.
    • CreatedLocations: the game has initialised the in-game locations.
    • CreatedSaveFile: the game has created the physical save files.
  • When loading an existing save slot:
    • SaveParsed: the game has read the raw save data into StardewValley.SaveGame.loaded. Not applicable when connecting to a multiplayer host. This is equivalent to StardewValley.SaveGame.getLoadEnumerator value 20.
    • SaveLoadedBasicInfo: the game has applied the basic save info (including player data). Not applicable when connecting to a multiplayer host. Note that some basic info (like daily luck) is not initialised at this point. This is equivalent to StardewValley.SaveGame.getLoadEnumerator value 36.
    • SaveAddedLocations: the game has added the location instances to the game, but hasn't restored their save data yet.
    • SaveLoadedLocations: the game has restored the in-game location data. Not applicable when connecting to a multiplayer host. This is equivalent to StardewValley.SaveGame.getLoadEnumerator value 50.
  • Preloaded: the final metadata has been loaded from the save file. This happens before the game applies problem fixes, checks for achievements, starts music, etc. Not applicable when connecting to a multiplayer host.
  • Loaded: the save is fully loaded, but the world may not be fully initialised yet.
  • Ready: the save is fully loaded, the world has been initialised, and Context.IsWorldReady is now true.
  • ReturningToTitle: The game is exiting the loaded save and returning to the title screen. This happens before it returns to title; the stage after it returns to title is None.
e.OldStage LoadStage The previous load stage. See comments for e.NewStage.
#UnvalidatedUpdateTicking
UnvalidatedUpdateTicked
Raised before/after the game updates its state (≈60 times per second), regardless of normal SMAPI validation. This event is not thread-safe and may be invoked while game logic is running asynchronously. Changes to game state in this method may crash the game or corrupt an in-progress save. Do not use this event unless you're fully aware of the context in which your code will be run. Using this event will trigger a warning in the SMAPI console.

Event arguments:

event arg type description
e.Ticks int The number of ticks elapsed since the game started, including the current tick.
e.IsOneSecond bool Whether e.TicksElapsed is a multiple of 60, which happens approximately once per second.
e.IsMultipleOf(int number) method returns bool Whether e.TicksElapsed is a multiple of the given number. This is mainly useful if you want to run logic intermittently (e.g., e.IsMultipleOf(30) for every half-second).

Advanced

Change monitoring

You may want to handle a change that doesn't have its own event (e.g., an NPC's heart event ends, a letter is added to the mailbox, etc). You can usually do that by handling a general event like UpdateTicked, and detecting when the value(s) you're watching changed. For example, here's a complete mod which logs a message when an in-game event ends:

/// <summary>The main entry point for the mod.</summary>
internal sealed class ModEntry : Mod
{
    /*********
    ** Fields
    *********/
    /// <summary>The in-game event detected on the last update tick.</summary>
    private Event LastEvent;


    /*********
    ** Public methods
    *********/
    /// <summary>The mod entry point, called after the mod is first loaded.</summary>
    /// <param name="helper">Provides simplified APIs for writing mods.</param>
    public override void Entry(IModHelper helper)
    {
        helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked;
    }


    /*********
    ** Private methods
    *********/
    /// <summary>The method invoked when the game updates its state.</summary>
    /// <param name="sender">The event sender.</param>
    /// <param name="e">The event arguments.</param>
    private void OnUpdateTicked(object sender, EventArgs e)
    {
        if (this.LastEvent != null && Game1.CurrentEvent == null)
            this.Monitor.Log($"Event {this.LastEvent.id} just ended!");

        this.LastEvent = Game1.CurrentEvent;
    }
}

Custom priority

SMAPI calls event handlers in the same order they're registered by default, so the first event handler registered is the first to receive the event each time. This isn't always predictable, since it depends on mod load order and when each mod registers their handlers. This order is also an implementation detail, so it's not guaranteed.

If you need more control over the order, you can specify an event priority using the [EventPriority] attribute: Low (after most handlers), Default, High (before most handlers), or a custom value (e.g., High + 1 is higher priority than High). You should only do this if strictly needed; depending on event handler order between mods is fragile (e.g., the other mod might change its priority too).

/// <summary>The main entry point for the mod.</summary>
internal sealed class ModEntry : Mod
{
    /*********
    ** Public methods
    *********/
    /// <summary>The mod entry point, called after the mod is first loaded.</summary>
    /// <param name="helper">Provides simplified APIs for writing mods.</param>
    public override void Entry(IModHelper helper)
    {
        helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked;
    }


    /*********
    ** Private methods
    *********/
    /// <summary>The method invoked when the game updates its state.</summary>
    /// <param name="sender">The event sender.</param>
    /// <param name="e">The event arguments.</param>
    [EventPriority(EventPriority.High)]
    private void OnUpdateTicked(object sender, EventArgs e)
    {
        this.Monitor.Log("Update!");
    }
}