add load stages immediately after game adds initial locations

This commit is contained in:
Jesse Plamondon-Willard 2021-02-28 12:01:11 -05:00
parent db011ee751
commit 944c03737e
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
3 changed files with 58 additions and 5 deletions

View File

@ -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). * 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 ## 3.9.2
Released 21 February 2021 for Stardew Valley 1.5.4 or later. Released 21 February 2021 for Stardew Valley 1.5.4 or later.

View File

@ -9,6 +9,9 @@ namespace StardewModdingAPI.Enums
/// <summary>The game is creating a new save slot, and has initialized the basic save info.</summary> /// <summary>The game is creating a new save slot, and has initialized the basic save info.</summary>
CreatedBasicInfo, 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> /// <summary>The game is creating a new save slot, and has initialized the in-game locations.</summary>
CreatedLocations, 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> /// <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, 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> /// <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, SaveLoadedLocations,

View File

@ -29,6 +29,9 @@ namespace StardewModdingAPI.Patches
/// <summary>A callback to invoke when the load stage changes.</summary> /// <summary>A callback to invoke when the load stage changes.</summary>
private static Action<LoadStage> OnStageChanged; 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 ** Accessors
@ -62,9 +65,16 @@ namespace StardewModdingAPI.Patches
prefix: new HarmonyMethod(this.GetType(), nameof(LoadContextPatch.Before_TitleMenu_CreatedNewCharacter)) 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( harmony.Patch(
original: AccessTools.Method(typeof(Game1), nameof(Game1.loadForNewGame)), 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)) postfix: new HarmonyMethod(this.GetType(), nameof(LoadContextPatch.After_Game1_LoadForNewGame))
); );
} }
@ -82,16 +92,49 @@ namespace StardewModdingAPI.Patches
return true; 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> /// <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> /// <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() private static void After_Game1_LoadForNewGame()
{ {
bool creating = LoadContextPatch.IsInLoadForNewGame = false;
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.currentMinigame is Intro) // creating save with intro
|| (Game1.activeClickableMenu is TitleMenu menu && LoadContextPatch.Reflection.GetField<bool>(menu, "transitioningCharacterCreationMenu").GetValue()); // creating save, skipped intro || (Game1.activeClickableMenu is TitleMenu menu && LoadContextPatch.Reflection.GetField<bool>(menu, "transitioningCharacterCreationMenu").GetValue()); // creating save, skipped intro
if (creating)
LoadContextPatch.OnStageChanged(LoadStage.CreatedLocations);
} }
} }
} }