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..9f2ad7b4 --- /dev/null +++ b/CJBItemSpawner/Framework/ItemSpawnerAPI.cs @@ -0,0 +1,90 @@ +using System; +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 + { + 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 methods + *********/ + /// + public void OpenItemSpawnerMenu() + { + Game1.activeClickableMenu = this.BuildMenu(); + } + + /// + public event EventHandler? VariantsRequested; + + /// + 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 + 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..23fee091 --- /dev/null +++ b/CJBItemSpawner/IItemSpawnerAPI.cs @@ -0,0 +1,37 @@ +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); + } + + /// + /// Open the item spawner menu. + /// + void OpenItemSpawnerMenu(); + + /// + /// 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..2b1c6a1f 100644 --- a/CJBItemSpawner/ModEntry.cs +++ b/CJBItemSpawner/ModEntry.cs @@ -29,10 +29,18 @@ internal class ModEntry : Mod /// Manages the gamepad text entry UI. private readonly TextEntryManager TextEntryManager = new(); + /// The API + private readonly ItemSpawnerAPI API; + /********* ** Public methods *********/ + public ModEntry() + { + this.API = new(this.BuildMenu); + } + /// public override void Entry(IModHelper helper) { @@ -65,6 +73,11 @@ public override void Entry(IModHelper helper) helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked; } + /// + public override object? GetApi() + { + return this.API; + } /********* ** Private methods @@ -122,7 +135,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));