diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2021-07-09 22:30:13 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2021-07-09 22:30:13 -0400 |
commit | 8f96a97f070d654764de3b138678d8f62707f485 (patch) | |
tree | 9a59953e01bfc27d0f124579ae897fabec46092f /src/SMAPI.Mods.ConsoleCommands | |
parent | 7dd3e37dec6924b1acd3f3045aefe5ebc8f91e8f (diff) | |
parent | 60af28760a6edc509b16e7c62aa8b00ba9798793 (diff) | |
download | SMAPI-8f96a97f070d654764de3b138678d8f62707f485.tar.gz SMAPI-8f96a97f070d654764de3b138678d8f62707f485.tar.bz2 SMAPI-8f96a97f070d654764de3b138678d8f62707f485.zip |
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI.Mods.ConsoleCommands')
-rw-r--r-- | src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs | 408 | ||||
-rw-r--r-- | src/SMAPI.Mods.ConsoleCommands/manifest.json | 4 |
2 files changed, 238 insertions, 174 deletions
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs index 34149209..0357fe6b 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs @@ -28,8 +28,10 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework ** Public methods *********/ /// <summary>Get all spawnable items.</summary> + /// <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() + public IEnumerable<SearchableItem> GetAll(ItemType[] itemTypes = null, bool includeVariants = true) { // // @@ -41,222 +43,246 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework // // - IEnumerable<SearchableItem> GetAllRaw() { + HashSet<ItemType> types = itemTypes?.Any() == true ? new HashSet<ItemType>(itemTypes) : null; + bool ShouldGet(ItemType type) => types == null || types.Contains(type); + // get tools - for (int q = Tool.stone; q <= Tool.iridium; q++) + if (ShouldGet(ItemType.Tool)) { - int quality = q; - - yield return this.TryCreate(ItemType.Tool, ToolFactory.axe, _ => ToolFactory.getToolFromDescription(ToolFactory.axe, quality)); - yield return this.TryCreate(ItemType.Tool, ToolFactory.hoe, _ => ToolFactory.getToolFromDescription(ToolFactory.hoe, quality)); - yield return this.TryCreate(ItemType.Tool, ToolFactory.pickAxe, _ => ToolFactory.getToolFromDescription(ToolFactory.pickAxe, quality)); - yield return this.TryCreate(ItemType.Tool, ToolFactory.wateringCan, _ => ToolFactory.getToolFromDescription(ToolFactory.wateringCan, quality)); - if (quality != Tool.iridium) - yield return this.TryCreate(ItemType.Tool, ToolFactory.fishingRod, _ => ToolFactory.getToolFromDescription(ToolFactory.fishingRod, quality)); + for (int q = Tool.stone; q <= Tool.iridium; q++) + { + int quality = q; + + yield return this.TryCreate(ItemType.Tool, ToolFactory.axe, _ => ToolFactory.getToolFromDescription(ToolFactory.axe, quality)); + yield return this.TryCreate(ItemType.Tool, ToolFactory.hoe, _ => ToolFactory.getToolFromDescription(ToolFactory.hoe, quality)); + yield return this.TryCreate(ItemType.Tool, ToolFactory.pickAxe, _ => ToolFactory.getToolFromDescription(ToolFactory.pickAxe, quality)); + yield return this.TryCreate(ItemType.Tool, ToolFactory.wateringCan, _ => ToolFactory.getToolFromDescription(ToolFactory.wateringCan, quality)); + if (quality != Tool.iridium) + yield return this.TryCreate(ItemType.Tool, ToolFactory.fishingRod, _ => ToolFactory.getToolFromDescription(ToolFactory.fishingRod, quality)); + } + yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset, _ => new MilkPail()); // these don't have any sort of ID, so we'll just assign some arbitrary ones + yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 1, _ => new Shears()); + yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 2, _ => new Pan()); + yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 3, _ => new Wand()); } - yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset, _ => new MilkPail()); // these don't have any sort of ID, so we'll just assign some arbitrary ones - yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 1, _ => new Shears()); - yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 2, _ => new Pan()); - yield return this.TryCreate(ItemType.Tool, this.CustomIDOffset + 3, _ => new Wand()); // clothing + if (ShouldGet(ItemType.Clothing)) { - // items - HashSet<int> clothingIds = new HashSet<int>(); - foreach (int id in Game1.clothingInformation.Keys) - { - if (id < 0) - continue; // placeholder data for character customization clothing below - - clothingIds.Add(id); + foreach (int id in this.GetShirtIds()) yield return this.TryCreate(ItemType.Clothing, id, p => new Clothing(p.ID)); - } - - // character customization shirts (some shirts in this range have no data, but game has special logic to handle them) - for (int id = 1000; id <= 1111; id++) - { - if (!clothingIds.Contains(id)) - yield return this.TryCreate(ItemType.Clothing, id, p => new Clothing(p.ID)); - } } // wallpapers - for (int id = 0; id < 112; id++) - yield return this.TryCreate(ItemType.Wallpaper, id, p => new Wallpaper(p.ID) { Category = SObject.furnitureCategory }); + if (ShouldGet(ItemType.Wallpaper)) + { + for (int id = 0; id < 112; id++) + yield return this.TryCreate(ItemType.Wallpaper, id, p => new Wallpaper(p.ID) { Category = SObject.furnitureCategory }); + } // flooring - for (int id = 0; id < 56; id++) - yield return this.TryCreate(ItemType.Flooring, id, p => new Wallpaper(p.ID, isFloor: true) { Category = SObject.furnitureCategory }); + if (ShouldGet(ItemType.Flooring)) + { + for (int id = 0; id < 56; id++) + yield return this.TryCreate(ItemType.Flooring, id, p => new Wallpaper(p.ID, isFloor: true) { Category = SObject.furnitureCategory }); + } // equipment - foreach (int id in this.TryLoad<int, string>("Data\\Boots").Keys) - yield return this.TryCreate(ItemType.Boots, id, p => new Boots(p.ID)); - foreach (int id in this.TryLoad<int, string>("Data\\hats").Keys) - yield return this.TryCreate(ItemType.Hat, id, p => new Hat(p.ID)); + if (ShouldGet(ItemType.Boots)) + { + foreach (int id in this.TryLoad<int, string>("Data\\Boots").Keys) + yield return this.TryCreate(ItemType.Boots, id, p => new Boots(p.ID)); + } + if (ShouldGet(ItemType.Hat)) + { + foreach (int id in this.TryLoad<int, string>("Data\\hats").Keys) + yield return this.TryCreate(ItemType.Hat, id, p => new Hat(p.ID)); + } // weapons - foreach (int id in this.TryLoad<int, string>("Data\\weapons").Keys) + if (ShouldGet(ItemType.Weapon)) { - yield return this.TryCreate(ItemType.Weapon, id, p => (p.ID >= 32 && p.ID <= 34) - ? (Item)new Slingshot(p.ID) - : new MeleeWeapon(p.ID) - ); + 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) + : new MeleeWeapon(p.ID) + ); + } } // furniture - foreach (int id in this.TryLoad<int, string>("Data\\Furniture").Keys) - yield return this.TryCreate(ItemType.Furniture, id, p => Furniture.GetFurnitureInstance(p.ID)); + if (ShouldGet(ItemType.Furniture)) + { + foreach (int id in this.TryLoad<int, string>("Data\\Furniture").Keys) + yield return this.TryCreate(ItemType.Furniture, id, p => Furniture.GetFurnitureInstance(p.ID)); + } // craftables - foreach (int id in Game1.bigCraftablesInformation.Keys) - yield return this.TryCreate(ItemType.BigCraftable, id, p => new SObject(Vector2.Zero, p.ID)); + if (ShouldGet(ItemType.BigCraftable)) + { + foreach (int id in Game1.bigCraftablesInformation.Keys) + yield return this.TryCreate(ItemType.BigCraftable, id, p => new SObject(Vector2.Zero, p.ID)); + } // objects - foreach (int id in Game1.objectInformation.Keys) + if (ShouldGet(ItemType.Object) || ShouldGet(ItemType.Ring)) { - string[] fields = Game1.objectInformation[id]?.Split('/'); - - // secret notes - if (id == 79) + foreach (int id in Game1.objectInformation.Keys) { - foreach (int secretNoteId in this.TryLoad<int, string>("Data\\SecretNotes").Keys) + string[] fields = Game1.objectInformation[id]?.Split('/'); + + // secret notes + if (id == 79) { - yield return this.TryCreate(ItemType.Object, this.CustomIDOffset + secretNoteId, _ => + if (ShouldGet(ItemType.Object)) { - SObject note = new SObject(79, 1); - note.name = $"{note.name} #{secretNoteId}"; - return note; - }); + 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; + }); + } + } } - } - - // ring - else if (id != 801 && fields?.Length >= 4 && fields[3] == "Ring") // 801 = wedding ring, which isn't an equippable ring - yield return this.TryCreate(ItemType.Ring, id, p => new Ring(p.ID)); - // item - else - { - // spawn main item - SObject item = null; - yield return this.TryCreate(ItemType.Object, id, p => + // ring + else if (id != 801 && fields?.Length >= 4 && fields[3] == "Ring") // 801 = wedding ring, which isn't an equippable ring { - return item = (p.ID == 812 // roe - ? new ColoredObject(p.ID, 1, Color.White) - : new SObject(p.ID, 1) - ); - }); - if (item == null) - continue; - - // flavored items - switch (item.Category) + if (ShouldGet(ItemType.Ring)) + yield return this.TryCreate(ItemType.Ring, id, p => new Ring(p.ID)); + } + + // item + else if (ShouldGet(ItemType.Object)) { - // 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: + // spawn main item + SObject item = null; + yield return this.TryCreate(ItemType.Object, id, p => + { + return item = (p.ID == 812 // roe + ? new ColoredObject(p.ID, 1, Color.White) + : new SObject(p.ID, 1) + ); + }); + if (item == null) + continue; + + // flavored items + if (includeVariants) + { + switch (item.Category) { - this.GetRoeContextTagLookups(out HashSet<string> simpleTags, out List<List<string>> complexTags); + // 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 } + }); - 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, _ => + // 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) { - roe = new ColoredObject(812, 1, color) + 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 = $"{input.Name} Roe", - preserve = { Value = SObject.PreserveType.Roe }, - preservedParentSheetIndex = { Value = input.ParentSheetIndex } + Name = $"{item.Name} Honey", + preservedParentSheetIndex = { item.ParentSheetIndex } }; - roe.Price += input.Price / 2; - return roe; + honey.Price += item.Price * 2; + return honey; }); + break; - // aged roe - if (roe != null && pair.Key != 698) // aged sturgeon roe is caviar, which is a separate item + // roe and aged roe (derived from FishPond.GetFishProduce) + case SObject.sellAtFishShopCategory when item.ParentSheetIndex == 812: { - yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 7 + item.ParentSheetIndex, _ => new ColoredObject(447, 1, color) + this.GetRoeContextTagLookups(out HashSet<string> simpleTags, out List<List<string>> complexTags); + + foreach (var pair in Game1.objectInformation) { - name = $"Aged {input.Name} Roe", - Category = -27, - preserve = { Value = SObject.PreserveType.AgedRoe }, - preservedParentSheetIndex = { Value = input.ParentSheetIndex }, - Price = roe.Price * 2 - }); + // 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; } - break; + } } } } @@ -333,5 +359,43 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework ? new Color(61, 55, 42) : (TailoringMenu.GetDyeColor(fish) ?? Color.Orange); } + + /// <summary>Get valid shirt IDs.</summary> + /// <remarks> + /// Shirts have a possible range of 1000–1999, but not all of those IDs are valid. There are two sets of IDs: + /// + /// <list type="number"> + /// <item> + /// Shirts which exist in <see cref="Game1.clothingInformation"/>. + /// </item> + /// <item> + /// Shirts with a dynamic ID and no entry in <see cref="Game1.clothingInformation"/>. These automatically + /// use the generic shirt entry with ID <c>-1</c> and are mapped to a calculated position in the + /// <c>Characters/Farmer/shirts</c> spritesheet. There's no constant we can use, but some known valid + /// ranges are 1000–1111 (used in <see cref="Farmer.changeShirt"/> for the customization screen and + /// 1000–1127 (used in <see cref="Utility.getShopStock"/> and <see cref="GameLocation.sandyShopStock"/>). + /// Based on the spritesheet, the max valid ID is 1299. + /// </item> + /// </list> + /// </remarks> + private IEnumerable<int> GetShirtIds() + { + // defined shirt items + foreach (int id in Game1.clothingInformation.Keys) + { + if (id < 0) + continue; // placeholder data for character customization clothing below + + yield return id; + } + + // dynamic shirts + HashSet<int> clothingIds = new HashSet<int>(Game1.clothingInformation.Keys); + for (int id = 1000; id <= 1299; id++) + { + if (!clothingIds.Contains(id)) + yield return id; + } + } } } diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json index e9513309..1781c40d 100644 --- a/src/SMAPI.Mods.ConsoleCommands/manifest.json +++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json @@ -1,9 +1,9 @@ { "Name": "Console Commands", "Author": "SMAPI", - "Version": "3.10.1", + "Version": "3.11.0", "Description": "Adds SMAPI console commands that let you manipulate the game.", "UniqueID": "SMAPI.ConsoleCommands", "EntryDll": "ConsoleCommands.dll", - "MinimumApiVersion": "3.10.1" + "MinimumApiVersion": "3.11.0" } |