From a4b9563892645e21a04872a1a082e24dee9bd958 Mon Sep 17 00:00:00 2001 From: Wren Date: Thu, 23 Jan 2025 21:39:08 -0500 Subject: [PATCH 1/2] Item Spawner API rough draft --- .../Framework/ItemData/ItemRepository.cs | 29 +++++++- CJBItemSpawner/Framework/ItemSpawnerAPI.cs | 68 +++++++++++++++++++ CJBItemSpawner/IItemSpawnerAPI.cs | 32 +++++++++ CJBItemSpawner/ModEntry.cs | 10 ++- 4 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 CJBItemSpawner/Framework/ItemSpawnerAPI.cs create mode 100644 CJBItemSpawner/IItemSpawnerAPI.cs diff --git a/CJBItemSpawner/Framework/ItemData/ItemRepository.cs b/CJBItemSpawner/Framework/ItemData/ItemRepository.cs index 73ff7d1b..53508e34 100644 --- a/CJBItemSpawner/Framework/ItemData/ItemRepository.cs +++ b/CJBItemSpawner/Framework/ItemData/ItemRepository.cs @@ -15,9 +15,20 @@ namespace CJBItemSpawner.Framework.ItemData; /// This is copied from the SMAPI source code and should be kept in sync with it. internal class ItemRepository { + /********* + ** Fields + *********/ + /// Used to communicate with other mods + private readonly ItemSpawnerAPI API; + /********* ** Public methods *********/ + public ItemRepository(ItemSpawnerAPI API) + { + this.API = API; + } + /// Get all spawnable items. /// Only include items for the given . /// Whether to include flavored variants like "Sunflower Honey". @@ -90,7 +101,8 @@ public IEnumerable GetAll(string? onlyType = null, bool includeV break; default: - if (result != null) + // skip blacklisted items + if (result != null && !this.API.IsBlacklisted(result.QualifiedItemId)) yield return result; break; } @@ -99,6 +111,9 @@ public IEnumerable GetAll(string? onlyType = null, bool includeV { foreach (SearchableItem? variant in this.GetFlavoredObjectVariants(objectDataDefinition, result?.Item as SObject, itemType)) yield return variant; + + foreach (SearchableItem? variant in this.API.GetVariantsFor("(O)", id, this.TryCreate)) + yield return variant; } } } @@ -108,7 +123,17 @@ public IEnumerable GetAll(string? onlyType = null, bool includeV // no special handling needed default: foreach (string id in itemType.GetAllIds()) - yield return this.TryCreate(itemType.Identifier, id, p => ItemRegistry.Create(itemType.Identifier + p.Id)); + { + // skip blacklisted items + if (!this.API.IsBlacklisted(itemType.Identifier + id)) + yield return this.TryCreate(itemType.Identifier, id, p => ItemRegistry.Create(itemType.Identifier + p.Id)); + + if (includeVariants) + { + foreach (SearchableItem? variant in this.API.GetVariantsFor(itemType.Identifier, id, this.TryCreate)) + yield return variant; + } + } break; } } diff --git a/CJBItemSpawner/Framework/ItemSpawnerAPI.cs b/CJBItemSpawner/Framework/ItemSpawnerAPI.cs new file mode 100644 index 00000000..c049bc60 --- /dev/null +++ b/CJBItemSpawner/Framework/ItemSpawnerAPI.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using CJBItemSpawner.Framework.ItemData; +using StardewValley; + +namespace CJBItemSpawner.Framework +{ + public class ItemSpawnerAPI : IItemSpawnerAPI + { + internal delegate SearchableItem? SearchableItemFactory(string type, string key, Func createItem); + + private readonly HashSet Blacklist = []; + + public class VariantsRequestedEventArgs : IItemSpawnerAPI.IVariantsRequestedEventArgs + { + private readonly SearchableItemFactory TryCreate; + private readonly string type; + internal readonly List Items = []; + + /// + public string BaseId { get; init; } + + /// + public void TryAddVariant(string variantId, Func createItem) + { + if (this.TryCreate(this.type,variantId, createItem) is SearchableItem result) + this.Items.Add(result); + } + + internal VariantsRequestedEventArgs(string type, string baseId, SearchableItemFactory tryCreate) + { + this.BaseId = baseId; + this.TryCreate = tryCreate; + this.type = type; + } + } + + /// + public event EventHandler? VariantsRequested; + + /// + public void BlacklistItem(string qualifiedId) + { + this.Blacklist.Add(qualifiedId); + } + + /// Gets API-Added variants for a given item. + /// The item type + /// The item's unqualified id + internal IEnumerable GetVariantsFor(string type, string baseId, SearchableItemFactory TryCreate) + { + // skip setup and teardown if nobody is using the api. + if (VariantsRequested is null) + return []; + + VariantsRequestedEventArgs args = new(type, baseId, TryCreate); + VariantsRequested(this, args); + return args.Items; + } + + /// Gets whether or not an item has been blacklisted. + /// The qualified id for the item + internal bool IsBlacklisted(string id) + { + return this.Blacklist.Contains(id); + } + } +} diff --git a/CJBItemSpawner/IItemSpawnerAPI.cs b/CJBItemSpawner/IItemSpawnerAPI.cs new file mode 100644 index 00000000..022e10d7 --- /dev/null +++ b/CJBItemSpawner/IItemSpawnerAPI.cs @@ -0,0 +1,32 @@ +using System; +using StardewValley; + +namespace CJBItemSpawner +{ + public interface IItemSpawnerAPI + { + public interface IVariantsRequestedEventArgs + { + /// The item to provide variants for + public string BaseId { get; } + + /// Add an item variant if valid + /// A unique variant identifier. Should include the base item id. + /// Creates an instance of the item + public void TryAddVariant(string variantId, Func createItem); + } + + /// + /// Prevent an item from being displayed in the item spawner. + /// Should only be used for placeholder items.
+ /// Does not disable variants for this item. + ///
+ /// The qualified item id + public void BlacklistItem(string qualifiedId); + + /// + /// Can be used to add custom variants to an existing item. + /// + public event EventHandler? VariantsRequested; + } +} diff --git a/CJBItemSpawner/ModEntry.cs b/CJBItemSpawner/ModEntry.cs index 4a6b8e6f..9a7fe7d4 100644 --- a/CJBItemSpawner/ModEntry.cs +++ b/CJBItemSpawner/ModEntry.cs @@ -29,6 +29,9 @@ internal class ModEntry : Mod /// Manages the gamepad text entry UI. private readonly TextEntryManager TextEntryManager = new(); + /// The API + private readonly ItemSpawnerAPI API = new(); + /********* ** Public methods @@ -65,6 +68,11 @@ public override void Entry(IModHelper helper) helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked; } + /// + public override object? GetApi() + { + return this.API; + } /********* ** Private methods @@ -122,7 +130,7 @@ private ItemMenu BuildMenu() /// Get the items which can be spawned. private IEnumerable GetSpawnableItems() { - foreach (SearchableItem entry in new ItemRepository().GetAll()) + foreach (SearchableItem entry in new ItemRepository(this.API).GetAll()) { ModDataCategory? category = this.Categories.FirstOrDefault(rule => rule.IsMatch(entry)); From 895f3cbdc0f7b69e495e452b11a92d5b46661e9d Mon Sep 17 00:00:00 2001 From: Wren Date: Thu, 23 Jan 2025 21:58:25 -0500 Subject: [PATCH 2/2] add open menu support --- CJBItemSpawner/Framework/ItemSpawnerAPI.cs | 22 ++++++++++++++++++++++ CJBItemSpawner/IItemSpawnerAPI.cs | 5 +++++ CJBItemSpawner/ModEntry.cs | 7 ++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CJBItemSpawner/Framework/ItemSpawnerAPI.cs b/CJBItemSpawner/Framework/ItemSpawnerAPI.cs index c049bc60..9f2ad7b4 100644 --- a/CJBItemSpawner/Framework/ItemSpawnerAPI.cs +++ b/CJBItemSpawner/Framework/ItemSpawnerAPI.cs @@ -2,14 +2,19 @@ using System.Collections.Generic; using CJBItemSpawner.Framework.ItemData; using StardewValley; +using StardewValley.Menus; namespace CJBItemSpawner.Framework { public class ItemSpawnerAPI : IItemSpawnerAPI { + /********* + ** Fields + *********/ internal delegate SearchableItem? SearchableItemFactory(string type, string key, Func createItem); private readonly HashSet Blacklist = []; + private readonly Func BuildMenu; public class VariantsRequestedEventArgs : IItemSpawnerAPI.IVariantsRequestedEventArgs { @@ -35,6 +40,15 @@ internal VariantsRequestedEventArgs(string type, string baseId, SearchableItemFa } } + /********* + ** Public methods + *********/ + /// + public void OpenItemSpawnerMenu() + { + Game1.activeClickableMenu = this.BuildMenu(); + } + /// public event EventHandler? VariantsRequested; @@ -44,6 +58,14 @@ public void BlacklistItem(string qualifiedId) this.Blacklist.Add(qualifiedId); } + /********* + ** Private methods + *********/ + internal ItemSpawnerAPI(Func BuildMenu) + { + this.BuildMenu = BuildMenu; + } + /// Gets API-Added variants for a given item. /// The item type /// The item's unqualified id diff --git a/CJBItemSpawner/IItemSpawnerAPI.cs b/CJBItemSpawner/IItemSpawnerAPI.cs index 022e10d7..23fee091 100644 --- a/CJBItemSpawner/IItemSpawnerAPI.cs +++ b/CJBItemSpawner/IItemSpawnerAPI.cs @@ -16,6 +16,11 @@ public interface IVariantsRequestedEventArgs public void TryAddVariant(string variantId, Func createItem); } + /// + /// Open the item spawner menu. + /// + void OpenItemSpawnerMenu(); + /// /// Prevent an item from being displayed in the item spawner. /// Should only be used for placeholder items.
diff --git a/CJBItemSpawner/ModEntry.cs b/CJBItemSpawner/ModEntry.cs index 9a7fe7d4..2b1c6a1f 100644 --- a/CJBItemSpawner/ModEntry.cs +++ b/CJBItemSpawner/ModEntry.cs @@ -30,12 +30,17 @@ internal class ModEntry : Mod private readonly TextEntryManager TextEntryManager = new(); /// The API - private readonly ItemSpawnerAPI API = new(); + private readonly ItemSpawnerAPI API; /********* ** Public methods *********/ + public ModEntry() + { + this.API = new(this.BuildMenu); + } + /// public override void Entry(IModHelper helper) {