diff --git a/docs/release-notes.md b/docs/release-notes.md
index fbd5df75..0f5547ff 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -18,7 +18,7 @@ For players:
For modders:
* **New event system.**
- SMAPI 3.0 removes the deprecated static events in favor of the new `helper.Events` API. The event engine has been rewritten to make events more efficient, add events that weren't possible before, make existing events more useful, and make event usage and behavior more consistent.
+ SMAPI 3.0 removes the deprecated static events in favor of the new `helper.Events` API. The event engine has been rewritten to make events more efficient, add events that weren't possible before, make existing events more useful, and make event usage and behavior more consistent. When a mod makes changes in an event handler, those changes are now also reflected in the next event raise.
* **Improved mod build package.**
The [mod build package](https://www.nuget.org/packages/Pathoschild.Stardew.ModBuildConfig) has been improved to include the `assets` folder by default if present, support the new `.csproj` project format, enable mod `.pdb` files automatically (to provide line numbers in error messages), add optional Harmony support, and fix some bugs and edge cases. This also adds compatibility with SMAPI 3.0 and Stardew Valley 1.4, and drops support for older versions.
@@ -30,7 +30,7 @@ For modders:
SMAPI now automatically detects when it's running on Android, and updates the `Constants.TargetPlatform` for mods to use.
* **Improved asset propagation.**
- SMAPI now automatically propagates content changes for farm animal data, critter textures, and `DayTimeMoneyBox` buttons.
+ SMAPI now automatically propagates content changes for farm animal data, critter textures, and `DayTimeMoneyBox` buttons. It also sets the `Texture2D.Name` field to the loaded asset key for all image assets, so mods can check which asset a texture was loaded for.
* **Breaking changes:**
See _[migrate to SMAPI 3.0](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_3.0)_ for more info.
@@ -91,16 +91,18 @@ For modders:
* Added `IContentPack.HasFile`, `Context.IsGameLaunched`, and `SemanticVersion.TryParse`.
* Added separate `LogNetworkTraffic` option to make verbose logging less overwhelmingly verbose.
* Added asset propagation for `Data\FarmAnimals`, critter textures, and `DayTimeMoneyBox` buttons.
+ * Added `Texture2D.Name` values set to the asset key.
* `Constants.TargetPlatform` now returns `Android` when playing on an Android device.
* Trace logs for a broken mod now list all detected issues (instead of the first one).
* Trace logs when loading mods are now more clear.
* Clarified update-check errors for mods with multiple update keys.
- * Fixed custom maps loaded from `.xnb` files not having their tilesheet paths automatically adjusted.
- * Fixed custom maps loaded from the mod folder with tilesheets in a subfolder not working crossplatform. All tilesheet paths are now normalized for the OS automatically.
* Updated dependencies (including Json.NET 11.0.2 → 12.0.2 and Mono.Cecil 0.10.1 → 0.11).
* Fixes:
+ * Fixed custom maps loaded from `.xnb` files not having their tilesheet paths automatically adjusted.
+ * Fixed custom maps loaded from the mod folder with tilesheets in a subfolder not working crossplatform. All tilesheet paths are now normalized for the OS automatically.
* Fixed issue where mod changes weren't tracked correctly for raising events in some cases. Events now reflect a frozen snapshot of the game state, and any mod changes are reflected in the next event tick.
* Fixed issue where, when a mod's `IAssetEditor` uses `asset.ReplaceWith` on a texture asset while playing in non-English, any changes from that point won't affect subsequent cached asset loads.
+ * Fixed asset propagation for NPC portraits resetting any unique portraits (e.g. Maru's hospital portrait) to the default.
* Fixed `LoadStageChanged` event not raising correct flags in some cases when creating a new save.
## 2.11.3
diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
index de39dbae..5283340e 100644
--- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
@@ -6,6 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Framework.Content;
using StardewModdingAPI.Framework.Exceptions;
using StardewModdingAPI.Framework.Reflection;
@@ -264,6 +265,11 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// The language code for which to inject the asset.
protected virtual void Inject(string assetName, T value, LanguageCode language)
{
+ // track asset key
+ if (value is Texture2D texture)
+ texture.Name = assetName;
+
+ // cache asset
assetName = this.AssertAndNormalizeAssetName(assetName);
this.Cache[assetName] = value;
}
diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs
index 66e6ed14..c0d57f4b 100644
--- a/src/SMAPI/Metadata/CoreAssetPropagator.cs
+++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs
@@ -507,6 +507,7 @@ namespace StardewModdingAPI.Metadata
// find matches
TAnimal[] animals = this.GetCharacters()
.OfType()
+ .Where(p => key == this.GetNormalizedPath(p.Sprite?.Texture?.Name))
.ToArray();
if (!animals.Any())
return false;
@@ -587,7 +588,7 @@ namespace StardewModdingAPI.Metadata
let locCritters = this.Reflection.GetField>(location, "critters").GetValue()
where locCritters != null
from Critter critter in locCritters
- where this.GetNormalizedPath(critter.sprite.textureName) == key
+ where this.GetNormalizedPath(critter.sprite?.Texture?.Name) == key
select critter
)
.ToArray();
@@ -673,7 +674,7 @@ namespace StardewModdingAPI.Metadata
// get NPCs
HashSet lookup = new HashSet(keys, StringComparer.InvariantCultureIgnoreCase);
NPC[] characters = this.GetCharacters()
- .Where(npc => npc.Sprite != null && lookup.Contains(this.GetNormalizedPath(npc.Sprite.textureName.Value)))
+ .Where(npc => npc.Sprite != null && lookup.Contains(this.GetNormalizedPath(npc.Sprite?.Texture?.Name)))
.ToArray();
if (!characters.Any())
return 0;
@@ -697,36 +698,23 @@ namespace StardewModdingAPI.Metadata
{
// get NPCs
HashSet lookup = new HashSet(keys, StringComparer.InvariantCultureIgnoreCase);
- var villagers =
- (
- from npc in this.GetCharacters()
- where npc.isVillager()
- let textureKey = this.GetNormalizedPath($"Portraits\\{this.getTextureName(npc)}")
- where lookup.Contains(textureKey)
- select new { npc, textureKey }
- )
+ var villagers = this
+ .GetCharacters()
+ .Where(npc => npc.isVillager() && lookup.Contains(this.GetNormalizedPath(npc.Portrait?.Name)))
.ToArray();
if (!villagers.Any())
return 0;
// update portrait
int reloaded = 0;
- foreach (var entry in villagers)
+ foreach (NPC npc in villagers)
{
- entry.npc.resetPortrait();
- entry.npc.Portrait = content.Load(entry.textureKey);
+ npc.Portrait = content.Load(npc.Portrait.Name);
reloaded++;
}
return reloaded;
}
- private string getTextureName(NPC npc)
- {
- string name = npc.Name;
- string str = name == "Old Mariner" ? "Mariner" : (name == "Dwarf King" ? "DwarfKing" : (name == "Mister Qi" ? "MrQi" : (name == "???" ? "Monsters\\Shadow Guy" : name)));
- return str;
- }
-
/// Reload tree textures.
/// The content manager through which to reload the asset.
/// The asset key to reload.