summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/release-notes.md4
-rw-r--r--src/SMAPI/Enums/LoadStage.cs6
-rw-r--r--src/SMAPI/Patches/LoadContextPatch.cs53
3 files changed, 58 insertions, 5 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md
index fb67d8dc..e7401910 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -7,6 +7,10 @@
* Migrated to Harmony 2.0 (see [_migrate to Harmony 2.0_](https://stardewvalleywiki.com/Modding:Migrate_to_Harmony_2.0) for more info).
-->
+## Upcoming release
+* For mod authors:
+ * Added two stages to the `LoadStageChanged` event: `CreatedInitialLocations` and `SaveAddedLocations`, raised immediately after the game adds its vanilla locations but before they're initialized.
+
## 3.9.2
Released 21 February 2021 for Stardew Valley 1.5.4 or later.
diff --git a/src/SMAPI/Enums/LoadStage.cs b/src/SMAPI/Enums/LoadStage.cs
index 5c2b0412..aa95d201 100644
--- a/src/SMAPI/Enums/LoadStage.cs
+++ b/src/SMAPI/Enums/LoadStage.cs
@@ -9,6 +9,9 @@ namespace StardewModdingAPI.Enums
/// <summary>The game is creating a new save slot, and has initialized the basic save info.</summary>
CreatedBasicInfo,
+ /// <summary>The game is creating a new save slot, and has added the location instances but hasn't fully initialized them yet.</summary>
+ CreatedInitialLocations,
+
/// <summary>The game is creating a new save slot, and has initialized the in-game locations.</summary>
CreatedLocations,
@@ -21,6 +24,9 @@ namespace StardewModdingAPI.Enums
/// <summary>The game is loading a save slot, and 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 initialized at this point. This is equivalent to <see cref="StardewValley.SaveGame.getLoadEnumerator"/> value 36.</summary>
SaveLoadedBasicInfo,
+ /// <summary>The game is loading a save slot and has added the location instances, but hasn't applied the data yet. Not applicable when connecting to a multiplayer host.</summary>
+ SaveAddedLocations,
+
/// <summary>The game is loading a save slot, and has applied the in-game location data. Not applicable when connecting to a multiplayer host. This is equivalent to <see cref="StardewValley.SaveGame.getLoadEnumerator"/> value 50.</summary>
SaveLoadedLocations,
diff --git a/src/SMAPI/Patches/LoadContextPatch.cs b/src/SMAPI/Patches/LoadContextPatch.cs
index ceda061b..9fddcc50 100644
--- a/src/SMAPI/Patches/LoadContextPatch.cs
+++ b/src/SMAPI/Patches/LoadContextPatch.cs
@@ -29,6 +29,9 @@ namespace StardewModdingAPI.Patches
/// <summary>A callback to invoke when the load stage changes.</summary>
private static Action<LoadStage> OnStageChanged;
+ /// <summary>Whether the game is running running the code in <see cref="Game1.loadForNewGame"/>.</summary>
+ private static bool IsInLoadForNewGame;
+
/*********
** Accessors
@@ -62,9 +65,16 @@ namespace StardewModdingAPI.Patches
prefix: new HarmonyMethod(this.GetType(), nameof(LoadContextPatch.Before_TitleMenu_CreatedNewCharacter))
);
- // detect CreatedLocations
+ // detect CreatedInitialLocations and SaveAddedLocations
+ harmony.Patch(
+ original: AccessTools.Method(typeof(Game1), nameof(Game1.AddModNPCs)),
+ prefix: new HarmonyMethod(this.GetType(), nameof(LoadContextPatch.Before_Game1_AddModNPCs))
+ );
+
+ // detect CreatedLocations, and track IsInLoadForNewGame
harmony.Patch(
original: AccessTools.Method(typeof(Game1), nameof(Game1.loadForNewGame)),
+ prefix: new HarmonyMethod(this.GetType(), nameof(LoadContextPatch.Before_Game1_LoadForNewGame)),
postfix: new HarmonyMethod(this.GetType(), nameof(LoadContextPatch.After_Game1_LoadForNewGame))
);
}
@@ -82,16 +92,49 @@ namespace StardewModdingAPI.Patches
return true;
}
+ /// <summary>Called before <see cref="Game1.AddModNPCs"/>.</summary>
+ /// <returns>Returns whether to execute the original method.</returns>
+ /// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
+ private static bool Before_Game1_AddModNPCs()
+ {
+ // When this method is called from Game1.loadForNewGame, it happens right after adding the vanilla
+ // locations but before initializing them.
+ if (LoadContextPatch.IsInLoadForNewGame)
+ {
+ LoadContextPatch.OnStageChanged(LoadContextPatch.IsCreating()
+ ? LoadStage.CreatedInitialLocations
+ : LoadStage.SaveAddedLocations
+ );
+ }
+
+ return true;
+ }
+
+ /// <summary>Called before <see cref="Game1.loadForNewGame"/>.</summary>
+ /// <returns>Returns whether to execute the original method.</returns>
+ /// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
+ private static bool Before_Game1_LoadForNewGame()
+ {
+ LoadContextPatch.IsInLoadForNewGame = true;
+ return true;
+ }
+
/// <summary>Called after <see cref="Game1.loadForNewGame"/>.</summary>
/// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
private static void After_Game1_LoadForNewGame()
{
- bool creating =
- (Game1.currentMinigame is Intro) // creating save with intro
- || (Game1.activeClickableMenu is TitleMenu menu && LoadContextPatch.Reflection.GetField<bool>(menu, "transitioningCharacterCreationMenu").GetValue()); // creating save, skipped intro
+ LoadContextPatch.IsInLoadForNewGame = false;
- if (creating)
+ if (LoadContextPatch.IsCreating())
LoadContextPatch.OnStageChanged(LoadStage.CreatedLocations);
}
+
+ /// <summary>Get whether the save file is currently being created.</summary>
+ private static bool IsCreating()
+ {
+ return
+ (Game1.currentMinigame is Intro) // creating save with intro
+ || (Game1.activeClickableMenu is TitleMenu menu && LoadContextPatch.Reflection.GetField<bool>(menu, "transitioningCharacterCreationMenu").GetValue()); // creating save, skipped intro
+ }
}
}