summaryrefslogtreecommitdiff
path: root/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-05-01 18:16:09 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-05-01 18:16:09 -0400
commitc8ad50dad1d706a1901798f9396f6becfea36c0e (patch)
tree28bd818a5db39ec5ece1bd141a28de955950463b /src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
parent451b70953ff4c0b1b27ae0de203ad99379b45b2a (diff)
parentf78093bdb58d477b400cde3f19b70ffd6ddf833d (diff)
downloadSMAPI-c8ad50dad1d706a1901798f9396f6becfea36c0e.tar.gz
SMAPI-c8ad50dad1d706a1901798f9396f6becfea36c0e.tar.bz2
SMAPI-c8ad50dad1d706a1901798f9396f6becfea36c0e.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs')
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs320
1 files changed, 189 insertions, 131 deletions
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
index 0357fe6b..3722e155 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
@@ -31,7 +31,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
/// <param name="itemTypes">The item types to fetch (or null for any type).</param>
/// <param name="includeVariants">Whether to include flavored variants like "Sunflower Honey".</param>
[SuppressMessage("ReSharper", "AccessToModifiedClosure", Justification = "TryCreate invokes the lambda immediately.")]
- public IEnumerable<SearchableItem> GetAll(ItemType[] itemTypes = null, bool includeVariants = true)
+ public IEnumerable<SearchableItem> GetAll(ItemType[]? itemTypes = null, bool includeVariants = true)
{
//
//
@@ -43,9 +43,9 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
//
//
- IEnumerable<SearchableItem> GetAllRaw()
+ IEnumerable<SearchableItem?> GetAllRaw()
{
- HashSet<ItemType> types = itemTypes?.Any() == true ? new HashSet<ItemType>(itemTypes) : null;
+ HashSet<ItemType>? types = itemTypes?.Any() == true ? new HashSet<ItemType>(itemTypes) : null;
bool ShouldGet(ItemType type) => types == null || types.Contains(type);
// get tools
@@ -106,8 +106,8 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
{
foreach (int id in this.TryLoad<int, string>("Data\\weapons").Keys)
{
- yield return this.TryCreate(ItemType.Weapon, id, p => (p.ID >= 32 && p.ID <= 34)
- ? (Item)new Slingshot(p.ID)
+ yield return this.TryCreate(ItemType.Weapon, id, p => p.ID is >= 32 and <= 34
+ ? new Slingshot(p.ID)
: new MeleeWeapon(p.ID)
);
}
@@ -132,37 +132,40 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
{
foreach (int id in Game1.objectInformation.Keys)
{
- string[] fields = Game1.objectInformation[id]?.Split('/');
+ string[]? fields = Game1.objectInformation[id]?.Split('/');
- // secret notes
- if (id == 79)
+ // ring
+ if (id != 801 && fields?.Length >= 4 && fields[3] == "Ring") // 801 = wedding ring, which isn't an equippable ring
+ {
+ if (ShouldGet(ItemType.Ring))
+ yield return this.TryCreate(ItemType.Ring, id, p => new Ring(p.ID));
+ }
+
+ // journal scrap
+ else if (id == 842)
{
if (ShouldGet(ItemType.Object))
{
- foreach (int secretNoteId in this.TryLoad<int, string>("Data\\SecretNotes").Keys)
- {
- yield return this.TryCreate(ItemType.Object, this.CustomIDOffset + secretNoteId, _ =>
- {
- SObject note = new SObject(79, 1);
- note.name = $"{note.name} #{secretNoteId}";
- return note;
- });
- }
+ foreach (SearchableItem? journalScrap in this.GetSecretNotes(isJournalScrap: true))
+ yield return journalScrap;
}
}
- // ring
- else if (id != 801 && fields?.Length >= 4 && fields[3] == "Ring") // 801 = wedding ring, which isn't an equippable ring
+ // secret notes
+ else if (id == 79)
{
- if (ShouldGet(ItemType.Ring))
- yield return this.TryCreate(ItemType.Ring, id, p => new Ring(p.ID));
+ if (ShouldGet(ItemType.Object))
+ {
+ foreach (SearchableItem? secretNote in this.GetSecretNotes(isJournalScrap: false))
+ yield return secretNote;
+ }
}
- // item
+ // object
else if (ShouldGet(ItemType.Object))
{
// spawn main item
- SObject item = null;
+ SObject? item = null;
yield return this.TryCreate(ItemType.Object, id, p =>
{
return item = (p.ID == 812 // roe
@@ -176,125 +179,179 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
// flavored items
if (includeVariants)
{
- switch (item.Category)
- {
- // fruit products
- case SObject.FruitsCategory:
- // wine
- yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 2 + item.ParentSheetIndex, _ => new SObject(348, 1)
- {
- Name = $"{item.Name} Wine",
- Price = item.Price * 3,
- preserve = { SObject.PreserveType.Wine },
- preservedParentSheetIndex = { item.ParentSheetIndex }
- });
-
- // jelly
- yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 3 + item.ParentSheetIndex, _ => new SObject(344, 1)
- {
- Name = $"{item.Name} Jelly",
- Price = 50 + item.Price * 2,
- preserve = { SObject.PreserveType.Jelly },
- preservedParentSheetIndex = { item.ParentSheetIndex }
- });
- break;
-
- // vegetable products
- case SObject.VegetableCategory:
- // juice
- yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 4 + item.ParentSheetIndex, _ => new SObject(350, 1)
- {
- Name = $"{item.Name} Juice",
- Price = (int)(item.Price * 2.25d),
- preserve = { SObject.PreserveType.Juice },
- preservedParentSheetIndex = { item.ParentSheetIndex }
- });
-
- // pickled
- yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + item.ParentSheetIndex, _ => new SObject(342, 1)
- {
- Name = $"Pickled {item.Name}",
- Price = 50 + item.Price * 2,
- preserve = { SObject.PreserveType.Pickle },
- preservedParentSheetIndex = { item.ParentSheetIndex }
- });
- break;
-
- // flower honey
- case SObject.flowersCategory:
- yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + item.ParentSheetIndex, _ =>
- {
- SObject honey = new SObject(Vector2.Zero, 340, $"{item.Name} Honey", false, true, false, false)
- {
- Name = $"{item.Name} Honey",
- preservedParentSheetIndex = { item.ParentSheetIndex }
- };
- honey.Price += item.Price * 2;
- return honey;
- });
- break;
-
- // roe and aged roe (derived from FishPond.GetFishProduce)
- case SObject.sellAtFishShopCategory when item.ParentSheetIndex == 812:
- {
- this.GetRoeContextTagLookups(out HashSet<string> simpleTags, out List<List<string>> complexTags);
-
- foreach (var pair in Game1.objectInformation)
- {
- // get input
- SObject input = this.TryCreate(ItemType.Object, pair.Key, p => new SObject(p.ID, 1))?.Item as SObject;
- var inputTags = input?.GetContextTags();
- if (inputTags?.Any() != true)
- continue;
-
- // check if roe-producing fish
- if (!inputTags.Any(tag => simpleTags.Contains(tag)) && !complexTags.Any(set => set.All(tag => input.HasContextTag(tag))))
- continue;
-
- // yield roe
- SObject roe = null;
- Color color = this.GetRoeColor(input);
- yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 7 + item.ParentSheetIndex, _ =>
- {
- roe = new ColoredObject(812, 1, color)
- {
- name = $"{input.Name} Roe",
- preserve = { Value = SObject.PreserveType.Roe },
- preservedParentSheetIndex = { Value = input.ParentSheetIndex }
- };
- roe.Price += input.Price / 2;
- return roe;
- });
-
- // aged roe
- if (roe != null && pair.Key != 698) // aged sturgeon roe is caviar, which is a separate item
- {
- yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 7 + item.ParentSheetIndex, _ => new ColoredObject(447, 1, color)
- {
- name = $"Aged {input.Name} Roe",
- Category = -27,
- preserve = { Value = SObject.PreserveType.AgedRoe },
- preservedParentSheetIndex = { Value = input.ParentSheetIndex },
- Price = roe.Price * 2
- });
- }
- }
- }
- break;
- }
+ foreach (SearchableItem? variant in this.GetFlavoredObjectVariants(item))
+ yield return variant;
}
}
}
}
}
- return GetAllRaw().Where(p => p != null);
+ return (
+ from item in GetAllRaw()
+ where item != null
+ select item
+ );
}
/*********
** Private methods
*********/
+ /// <summary>Get the individual secret note or journal scrap items.</summary>
+ /// <param name="isJournalScrap">Whether to get journal scraps.</param>
+ /// <remarks>Derived from <see cref="GameLocation.tryToCreateUnseenSecretNote"/>.</remarks>
+ private IEnumerable<SearchableItem?> GetSecretNotes(bool isJournalScrap)
+ {
+ // get base item ID
+ int baseId = isJournalScrap ? 842 : 79;
+
+ // get secret note IDs
+ var ids = this
+ .TryLoad<int, string>("Data\\SecretNotes")
+ .Keys
+ .Where(isJournalScrap
+ ? id => (id >= GameLocation.JOURNAL_INDEX)
+ : id => (id < GameLocation.JOURNAL_INDEX)
+ )
+ .Select<int, int>(isJournalScrap
+ ? id => (id - GameLocation.JOURNAL_INDEX)
+ : id => id
+ );
+
+ // build items
+ foreach (int id in ids)
+ {
+ int fakeId = this.CustomIDOffset * 8 + id;
+ if (isJournalScrap)
+ fakeId += GameLocation.JOURNAL_INDEX;
+
+ yield return this.TryCreate(ItemType.Object, fakeId, _ =>
+ {
+ SObject note = new(baseId, 1);
+ note.Name = $"{note.Name} #{id}";
+ return note;
+ });
+ }
+ }
+
+ /// <summary>Get flavored variants of a base item (like Blueberry Wine for Blueberry), if any.</summary>
+ /// <param name="item">A sample of the base item.</param>
+ private IEnumerable<SearchableItem?> GetFlavoredObjectVariants(SObject item)
+ {
+ int id = item.ParentSheetIndex;
+
+ switch (item.Category)
+ {
+ // fruit products
+ case SObject.FruitsCategory:
+ // wine
+ yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 2 + id, _ => new SObject(348, 1)
+ {
+ Name = $"{item.Name} Wine",
+ Price = item.Price * 3,
+ preserve = { SObject.PreserveType.Wine },
+ preservedParentSheetIndex = { id }
+ });
+
+ // jelly
+ yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 3 + id, _ => new SObject(344, 1)
+ {
+ Name = $"{item.Name} Jelly",
+ Price = 50 + item.Price * 2,
+ preserve = { SObject.PreserveType.Jelly },
+ preservedParentSheetIndex = { id }
+ });
+ break;
+
+ // vegetable products
+ case SObject.VegetableCategory:
+ // juice
+ yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 4 + id, _ => new SObject(350, 1)
+ {
+ Name = $"{item.Name} Juice",
+ Price = (int)(item.Price * 2.25d),
+ preserve = { SObject.PreserveType.Juice },
+ preservedParentSheetIndex = { id }
+ });
+
+ // pickled
+ yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + id, _ => new SObject(342, 1)
+ {
+ Name = $"Pickled {item.Name}",
+ Price = 50 + item.Price * 2,
+ preserve = { SObject.PreserveType.Pickle },
+ preservedParentSheetIndex = { id }
+ });
+ break;
+
+ // flower honey
+ case SObject.flowersCategory:
+ yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + id, _ =>
+ {
+ SObject honey = new(Vector2.Zero, 340, $"{item.Name} Honey", false, true, false, false)
+ {
+ Name = $"{item.Name} Honey",
+ preservedParentSheetIndex = { id }
+ };
+ honey.Price += item.Price * 2;
+ return honey;
+ });
+ break;
+
+ // roe and aged roe (derived from FishPond.GetFishProduce)
+ case SObject.sellAtFishShopCategory when id == 812:
+ {
+ this.GetRoeContextTagLookups(out HashSet<string> simpleTags, out List<List<string>> complexTags);
+
+ foreach (var pair in Game1.objectInformation)
+ {
+ // get input
+ SObject? input = this.TryCreate(ItemType.Object, pair.Key, p => new SObject(p.ID, 1))?.Item as SObject;
+ if (input == null)
+ continue;
+
+ HashSet<string> inputTags = input.GetContextTags();
+ if (!inputTags.Any())
+ continue;
+
+ // check if roe-producing fish
+ if (!inputTags.Any(tag => simpleTags.Contains(tag)) && !complexTags.Any(set => set.All(tag => input.HasContextTag(tag))))
+ continue;
+
+ // yield roe
+ SObject? roe = null;
+ Color color = this.GetRoeColor(input);
+ yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 7 + id, _ =>
+ {
+ roe = new ColoredObject(812, 1, color)
+ {
+ name = $"{input.Name} Roe",
+ preserve = { Value = SObject.PreserveType.Roe },
+ preservedParentSheetIndex = { Value = input.ParentSheetIndex }
+ };
+ roe.Price += input.Price / 2;
+ return roe;
+ });
+
+ // aged roe
+ if (roe != null && pair.Key != 698) // aged sturgeon roe is caviar, which is a separate item
+ {
+ yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 7 + id, _ => new ColoredObject(447, 1, color)
+ {
+ name = $"Aged {input.Name} Roe",
+ Category = -27,
+ preserve = { Value = SObject.PreserveType.AgedRoe },
+ preservedParentSheetIndex = { Value = input.ParentSheetIndex },
+ Price = roe.Price * 2
+ });
+ }
+ }
+ }
+ break;
+ }
+ }
+
/// <summary>Get optimized lookups to match items which produce roe in a fish pond.</summary>
/// <param name="simpleTags">A lookup of simple singular tags which match a roe-producing fish.</param>
/// <param name="complexTags">A list of tag sets which match roe-producing fish.</param>
@@ -320,6 +377,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
/// <typeparam name="TValue">The asset value type.</typeparam>
/// <param name="assetName">The data asset name.</param>
private Dictionary<TKey, TValue> TryLoad<TKey, TValue>(string assetName)
+ where TKey : notnull
{
try
{
@@ -336,7 +394,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
/// <param name="type">The item type.</param>
/// <param name="id">The unique ID (if different from the item's parent sheet index).</param>
/// <param name="createItem">Create an item instance.</param>
- private SearchableItem TryCreate(ItemType type, int id, Func<SearchableItem, Item> createItem)
+ private SearchableItem? TryCreate(ItemType type, int id, Func<SearchableItem, Item> createItem)
{
try
{