From 20b778390051569aa34a9ac42f03cd9b9a051df7 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 24 Mar 2018 20:26:33 -0400 Subject: update NPC textures when changed through the content API (#459) --- src/SMAPI/Metadata/CoreAssetPropagator.cs | 122 ++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs index 21aaeb6c..4a1d6097 100644 --- a/src/SMAPI/Metadata/CoreAssetPropagator.cs +++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs @@ -323,12 +323,18 @@ namespace StardewModdingAPI.Metadata return this.ReloadTreeTextures(content, key, Tree.pineTree); } - // building textures + // dynamic textures if (key.StartsWith(this.GetNormalisedPath("Buildings\\"), StringComparison.InvariantCultureIgnoreCase)) - { - string type = Path.GetFileName(key); - return this.ReloadBuildings(content, key, type); - } + return this.ReloadBuildings(content, key); + + if (key.StartsWith(this.GetNormalisedPath("Characters\\")) && this.CountSegments(key) == 2) // ignore Characters/Dialogue/*, etc + return this.ReloadNpcSprites(content, key, monster: false); + + if (key.StartsWith(this.GetNormalisedPath("Characters\\Monsters\\"))) + return this.ReloadNpcSprites(content, key, monster: true); + + if (key.StartsWith(this.GetNormalisedPath("Portraits\\"))) + return this.ReloadNpcPortraits(content, key); return false; } @@ -340,16 +346,18 @@ namespace StardewModdingAPI.Metadata /// Reload building textures. /// The content manager through which to reload the asset. /// The asset key to reload. - /// The type to reload. /// Returns whether any textures were reloaded. - private bool ReloadBuildings(LocalizedContentManager content, string key, string type) + private bool ReloadBuildings(LocalizedContentManager content, string key) { + // get buildings + string type = Path.GetFileName(key); Building[] buildings = Game1.locations .OfType() .SelectMany(p => p.buildings) .Where(p => p.buildingType == type) .ToArray(); + // reload buildings if (buildings.Any()) { Lazy texture = new Lazy(() => content.Load(key)); @@ -364,6 +372,61 @@ namespace StardewModdingAPI.Metadata return false; } + /// Reload the sprites for matching NPCs. + /// The content manager through which to reload the asset. + /// The asset key to reload. + /// Whether to match monsters (true) or non-monsters (false). + /// Returns whether any textures were reloaded. + private bool ReloadNpcSprites(LocalizedContentManager content, string key, bool monster) + { + // get NPCs + string name = this.GetNpcNameFromFileName(Path.GetFileName(key)); + NPC[] characters = + ( + from location in this.GetLocations() + from npc in location.characters + where npc.name == name && npc.IsMonster == monster + select npc + ) + .Distinct() + .ToArray(); + if (!characters.Any()) + return false; + + // update portrait + Texture2D texture = content.Load(key); + foreach (NPC character in characters) + character.Sprite.Texture = texture; + return true; + } + + /// Reload the portraits for matching NPCs. + /// The content manager through which to reload the asset. + /// The asset key to reload. + /// Returns whether any textures were reloaded. + private bool ReloadNpcPortraits(LocalizedContentManager content, string key) + { + // get NPCs + string name = this.GetNpcNameFromFileName(Path.GetFileName(key)); + NPC[] villagers = + ( + from location in this.GetLocations() + from npc in location.characters + where npc.name == name && npc.isVillager() + select npc + ) + .Distinct() + .ToArray(); + if (!villagers.Any()) + return false; + + // update portrait + Texture2D texture = content.Load(key); + foreach (NPC villager in villagers) + villager.Portrait = texture; + return true; + } + /// Reload tree textures. /// The content manager through which to reload the asset. /// The asset key to reload. @@ -390,5 +453,50 @@ namespace StardewModdingAPI.Metadata return false; } + + /// Get an NPC name from the name of their file under Content/Characters. + /// The file name. + /// Derived from . + private string GetNpcNameFromFileName(string name) + { + switch (name) + { + case "Mariner": + return "Old Mariner"; + case "DwarfKing": + return "Dwarf King"; + case "MrQi": + return "Mister Qi"; + default: + return name; + } + } + + /// Get all locations in the game. + private IEnumerable GetLocations() + { + foreach (GameLocation location in Game1.locations) + { + yield return location; + + if (location is BuildableGameLocation buildableLocation) + { + foreach (Building building in buildableLocation.buildings) + { + if (building.indoors != null) + yield return building.indoors; + } + } + } + } + + /// Count the number of segments in a path (e.g. 'a/b' is 2). + /// The path to check. + private int CountSegments(string path) + { + if (path == null) + return 0; + return path.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Length; + } } } -- cgit