diff options
Diffstat (limited to 'src/SMAPI.Mods.ConsoleCommands/Framework')
31 files changed, 303 insertions, 204 deletions
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs index 7e157c38..66f2f105 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands @@ -52,7 +53,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands /// <param name="value">The parsed value.</param> /// <param name="required">Whether to show an error if the argument is missing.</param> /// <param name="oneOf">Require that the argument match one of the given values (case-insensitive).</param> - public bool TryGet(int index, string name, out string value, bool required = true, string[] oneOf = null) + public bool TryGet(int index, string name, [NotNullWhen(true)] out string? value, bool required = true, string[]? oneOf = null) { value = null; @@ -86,7 +87,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands value = 0; // get argument - if (!this.TryGet(index, name, out string raw, required)) + if (!this.TryGet(index, name, out string? raw, required)) return false; // parse diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ConsoleCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ConsoleCommand.cs index 01cab92e..44b7824e 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ConsoleCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ConsoleCommand.cs @@ -100,7 +100,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands List<string[]> lines = new List<string[]>(rows.Length + 2) { header, - header.Select((value, i) => "".PadRight(widths[i], '-')).ToArray() + header.Select((_, i) => "".PadRight(widths[i], '-')).ToArray() }; lines.AddRange(rows); diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs index 957b0e75..f2194cff 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other { /// <summary>A command which runs one of the game's save migrations.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class ApplySaveFixCommand : ConsoleCommand { /********* @@ -21,7 +23,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other public override void Handle(IMonitor monitor, string command, ArgumentParser args) { // get fix ID - if (!args.TryGet(0, "fix_id", out string rawFixId, required: false)) + if (!args.TryGet(0, "fix_id", out string? rawFixId, required: false)) { monitor.Log("Invalid usage. Type 'help apply_save_fix' for details.", LogLevel.Error); return; diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/DebugCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/DebugCommand.cs index 1955c14e..cf1dcbce 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/DebugCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/DebugCommand.cs @@ -1,8 +1,10 @@ -using StardewValley; +using System.Diagnostics.CodeAnalysis; +using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other { /// <summary>A command which sends a debug command to the game.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class DebugCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/RegenerateBundles.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/RegenerateBundles.cs index 9beedb96..159d7c4a 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/RegenerateBundles.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/RegenerateBundles.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using Netcode; @@ -9,6 +10,7 @@ using StardewValley.Network; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other { /// <summary>A command which regenerates the game's bundles.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class RegenerateBundlesCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowDataFilesCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowDataFilesCommand.cs index 27f6ce53..a233d588 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowDataFilesCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowDataFilesCommand.cs @@ -1,8 +1,10 @@ -using System.Diagnostics; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other { /// <summary>A command which shows the data files.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class ShowDataFilesCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowGameFilesCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowGameFilesCommand.cs index 71093184..745b821b 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowGameFilesCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowGameFilesCommand.cs @@ -1,8 +1,10 @@ -using System.Diagnostics; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other { /// <summary>A command which shows the game files.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class ShowGameFilesCommand : ConsoleCommand { /********* @@ -18,8 +20,8 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other /// <param name="args">The command arguments.</param> public override void Handle(IMonitor monitor, string command, ArgumentParser args) { - Process.Start(Constants.ExecutionPath); - monitor.Log($"OK, opening {Constants.ExecutionPath}.", LogLevel.Info); + Process.Start(Constants.GamePath); + monitor.Log($"OK, opening {Constants.GamePath}.", LogLevel.Info); } } } diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs index 46583dc3..8bf9f5db 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other { /// <summary>A command which logs the keys being pressed for 30 seconds once enabled.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class TestInputCommand : ConsoleCommand { /********* @@ -37,9 +39,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other public override void OnUpdated(IMonitor monitor) { // handle expiry - if (this.ExpiryTicks == null) - return; - if (this.ExpiryTicks <= DateTime.UtcNow.Ticks) + if (this.ExpiryTicks != null && this.ExpiryTicks <= DateTime.UtcNow.Ticks) { monitor.Log("No longer logging input.", LogLevel.Info); this.ExpiryTicks = null; diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs index 0e8f7517..74d3d9df 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs @@ -13,7 +13,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player ** Fields *********/ /// <summary>Provides methods for searching and constructing items.</summary> - private readonly ItemRepository Items = new ItemRepository(); + private readonly ItemRepository Items = new(); /// <summary>The type names recognized by this command.</summary> private readonly string[] ValidTypes = Enum.GetNames(typeof(ItemType)).Concat(new[] { "Name" }).ToArray(); @@ -40,7 +40,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player } // read arguments - if (!args.TryGet(0, "item type", out string type, oneOf: this.ValidTypes)) + if (!args.TryGet(0, "item type", out string? type, oneOf: this.ValidTypes)) return; if (!args.TryGetInt(2, "count", out int count, min: 1, required: false)) count = 1; @@ -48,7 +48,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player quality = Object.lowQuality; // find matching item - SearchableItem match = Enum.TryParse(type, true, out ItemType itemType) + SearchableItem? match = Enum.TryParse(type, true, out ItemType itemType) ? this.FindItemByID(monitor, args, itemType) : this.FindItemByName(monitor, args); if (match == null) @@ -76,14 +76,14 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="args">The command arguments.</param> /// <param name="type">The item type.</param> - private SearchableItem FindItemByID(IMonitor monitor, ArgumentParser args, ItemType type) + private SearchableItem? FindItemByID(IMonitor monitor, ArgumentParser args, ItemType type) { // read arguments if (!args.TryGetInt(1, "item ID", out int id, min: 0)) return null; // find matching item - SearchableItem item = this.Items.GetAll().FirstOrDefault(p => p.Type == type && p.ID == id); + SearchableItem? item = this.Items.GetAll().FirstOrDefault(p => p.Type == type && p.ID == id); if (item == null) monitor.Log($"There's no {type} item with ID {id}.", LogLevel.Error); return item; @@ -92,10 +92,10 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player /// <summary>Get a matching item by its name.</summary> /// <param name="monitor">Writes messages to the console and log file.</param> /// <param name="args">The command arguments.</param> - private SearchableItem FindItemByName(IMonitor monitor, ArgumentParser args) + private SearchableItem? FindItemByName(IMonitor monitor, ArgumentParser args) { // read arguments - if (!args.TryGet(1, "item name", out string name)) + if (!args.TryGet(1, "item name", out string? name)) return null; // find matching items diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs index 1f12e5f9..ef35ad19 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs @@ -1,16 +1,18 @@ -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which list item types.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class ListItemTypesCommand : ConsoleCommand { /********* ** Fields *********/ /// <summary>Provides methods for searching and constructing items.</summary> - private readonly ItemRepository Items = new ItemRepository(); + private readonly ItemRepository Items = new(); /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs index 67569298..73d5b79d 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs @@ -1,18 +1,20 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which list items available to spawn.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class ListItemsCommand : ConsoleCommand { /********* ** Fields *********/ /// <summary>Provides methods for searching and constructing items.</summary> - private readonly ItemRepository Items = new ItemRepository(); + private readonly ItemRepository Items = new(); /********* @@ -59,15 +61,14 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player private IEnumerable<SearchableItem> GetItems(string[] searchWords) { // normalize search term - searchWords = searchWords?.Where(word => !string.IsNullOrWhiteSpace(word)).ToArray(); - if (searchWords?.Any() == false) - searchWords = null; + searchWords = searchWords.Where(word => !string.IsNullOrWhiteSpace(word)).ToArray(); + bool getAll = !searchWords.Any(); // find matches return ( from item in this.Items.GetAll() let term = $"{item.ID}|{item.Type}|{item.Name}|{item.DisplayName}" - where searchWords == null || searchWords.All(word => term.IndexOf(word, StringComparison.CurrentCultureIgnoreCase) != -1) + where getAll || searchWords.All(word => term.IndexOf(word, StringComparison.CurrentCultureIgnoreCase) != -1) select item ); } diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetColorCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetColorCommand.cs index 7b7cbf83..12a51bc9 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetColorCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetColorCommand.cs @@ -1,9 +1,11 @@ +using System.Diagnostics.CodeAnalysis; using Microsoft.Xna.Framework; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which edits the color of a player feature.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetColorCommand : ConsoleCommand { /********* @@ -20,9 +22,9 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player public override void Handle(IMonitor monitor, string command, ArgumentParser args) { // parse arguments - if (!args.TryGet(0, "target", out string target, oneOf: new[] { "hair", "eyes", "pants" })) + if (!args.TryGet(0, "target", out string? target, oneOf: new[] { "hair", "eyes", "pants" })) return; - if (!args.TryGet(1, "color", out string rawColor)) + if (!args.TryGet(1, "color", out string? rawColor)) return; // parse color diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs index 6b3b27cd..b2035d42 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Text; @@ -10,6 +11,7 @@ using StardewValley.GameData; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which changes the player's farm type.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetFarmTypeCommand : ConsoleCommand { /********* @@ -33,7 +35,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player } // parse arguments - if (!args.TryGet(0, "farm type", out string farmType)) + if (!args.TryGet(0, "farm type", out string? farmType)) return; bool isVanillaId = int.TryParse(farmType, out int vanillaId) && vanillaId is (>= 0 and < Farm.layout_max); @@ -108,7 +110,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player return; } - if (!this.GetCustomFarmTypes().TryGetValue(id, out ModFarmType customFarmType)) + if (!this.GetCustomFarmTypes().TryGetValue(id, out ModFarmType? customFarmType)) { monitor.Log($"Invalid farm type '{id}'. Enter `help set_farm_type` for more info.", LogLevel.Error); return; @@ -121,7 +123,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player /// <summary>Change the farm type.</summary> /// <param name="type">The farm type ID.</param> /// <param name="customFarmData">The custom farm type data, if applicable.</param> - private void SetFarmType(int type, ModFarmType customFarmData) + private void SetFarmType(int type, ModFarmType? customFarmData) { // set flags Game1.whichFarm = type; @@ -131,9 +133,10 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player Farm farm = Game1.getFarm(); farm.mapPath.Value = $@"Maps\{Farm.getMapNameFromTypeInt(Game1.whichFarm)}"; farm.reloadMap(); + farm.updateWarps(); // clear spouse area cache to avoid errors - FieldInfo cacheField = farm.GetType().GetField("_baseSpouseAreaTiles", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + FieldInfo? cacheField = farm.GetType().GetField("_baseSpouseAreaTiles", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); if (cacheField == null) throw new InvalidOperationException("Failed to access '_baseSpouseAreaTiles' field to clear spouse area cache."); if (cacheField.GetValue(farm) is not IDictionary cache) @@ -161,7 +164,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player /// <param name="type">The farm type.</param> private string GetVanillaName(int type) { - string translationKey = type switch + string? translationKey = type switch { Farm.default_layout => "Character_FarmStandard", Farm.riverlands_layout => "Character_FarmFishing", @@ -194,7 +197,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player ****/ /// <summary>Get the display name for a custom farm type.</summary> /// <param name="farmType">The custom farm type.</param> - private string GetCustomName(ModFarmType farmType) + private string? GetCustomName(ModFarmType? farmType) { if (string.IsNullOrWhiteSpace(farmType?.TooltipStringPath)) return farmType?.ID; diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs index f27b336f..f169159f 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs @@ -1,9 +1,11 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which edits the player's current health.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetHealthCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetImmunityCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetImmunityCommand.cs index df90adf2..1065bd21 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetImmunityCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetImmunityCommand.cs @@ -1,9 +1,11 @@ -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which edits the player's current immunity.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetImmunityCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxHealthCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxHealthCommand.cs index a5f7f444..c2c4931d 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxHealthCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxHealthCommand.cs @@ -1,9 +1,11 @@ -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which edits the player's maximum health.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetMaxHealthCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxStaminaCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxStaminaCommand.cs index e3c2f011..8c794e75 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxStaminaCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxStaminaCommand.cs @@ -1,9 +1,11 @@ -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which edits the player's maximum stamina.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetMaxStaminaCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs index 787ce920..3afcc62b 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs @@ -1,9 +1,11 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which edits the player's current money.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetMoneyCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetNameCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetNameCommand.cs index 4911ad1c..37c02ed0 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetNameCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetNameCommand.cs @@ -1,8 +1,10 @@ +using System.Diagnostics.CodeAnalysis; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which edits the player's name.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetNameCommand : ConsoleCommand { /********* @@ -19,9 +21,9 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player public override void Handle(IMonitor monitor, string command, ArgumentParser args) { // parse arguments - if (!args.TryGet(0, "target", out string target, oneOf: new[] { "player", "farm" })) + if (!args.TryGet(0, "target", out string? target, oneOf: new[] { "player", "farm" })) return; - args.TryGet(1, "name", out string name, required: false); + args.TryGet(1, "name", out string? name, required: false); // handle switch (target) diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs index c78378ef..24718ace 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs @@ -1,9 +1,11 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which edits the player's current stamina.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetStaminaCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStyleCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStyleCommand.cs index 98f6c330..8193ff27 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStyleCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStyleCommand.cs @@ -1,8 +1,10 @@ +using System.Diagnostics.CodeAnalysis; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player { /// <summary>A command which edits a player style.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetStyleCommand : ConsoleCommand { /********* @@ -19,7 +21,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player public override void Handle(IMonitor monitor, string command, ArgumentParser args) { // parse arguments - if (!args.TryGet(0, "target", out string target, oneOf: new[] { "hair", "shirt", "acc", "skin", "shoe", "swim", "gender" })) + if (!args.TryGet(0, "target", out string? target, oneOf: new[] { "hair", "shirt", "acc", "skin", "shoe", "swim", "gender" })) return; if (!args.TryGetInt(1, "style ID", out int styleID)) return; diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs index ceaeb278..4905b89a 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs @@ -1,5 +1,7 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using Microsoft.Xna.Framework; using StardewValley; using StardewValley.Locations; using StardewValley.Objects; @@ -9,6 +11,7 @@ using SObject = StardewValley.Object; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { /// <summary>A command which clears in-game objects.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class ClearCommand : ConsoleCommand { /********* @@ -49,13 +52,13 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World } // parse arguments - if (!args.TryGet(0, "location", out string locationName, required: true)) + if (!args.TryGet(0, "location", out string? locationName, required: true)) return; - if (!args.TryGet(1, "object type", out string type, required: true, oneOf: this.ValidTypes)) + if (!args.TryGet(1, "object type", out string? type, required: true, oneOf: this.ValidTypes)) return; // get target location - GameLocation location = Game1.locations.FirstOrDefault(p => p.Name != null && p.Name.Equals(locationName, StringComparison.OrdinalIgnoreCase)); + GameLocation? location = Game1.locations.FirstOrDefault(p => p.Name != null && p.Name.Equals(locationName, StringComparison.OrdinalIgnoreCase)); if (location == null && locationName == "current") location = Game1.currentLocation; if (location == null) @@ -92,11 +95,10 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World removed += this.RemoveObjects(location, obj => - !(obj is Chest) + obj is not Chest && ( - obj.Name == "Weeds" - || obj.Name == "Stone" - || (obj.ParentSheetIndex == 294 || obj.ParentSheetIndex == 295) + obj.Name is "Weeds" or "Stone" + || obj.ParentSheetIndex is 294 or 295 ) ) + this.RemoveResourceClumps(location, clump => this.DebrisClumps.Contains(clump.parentSheetIndex.Value)); @@ -114,7 +116,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World case "furniture": { - int removed = this.RemoveFurniture(location, furniture => true); + int removed = this.RemoveFurniture(location, _ => true); monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info); break; } @@ -138,11 +140,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { bool everything = type == "everything"; int removed = - this.RemoveFurniture(location, p => true) - + this.RemoveObjects(location, p => true) - + this.RemoveTerrainFeatures(location, p => true) - + this.RemoveLargeTerrainFeatures(location, p => everything || !(p is Bush bush) || bush.isDestroyable(location, p.currentTileLocation)) - + this.RemoveResourceClumps(location, p => true); + this.RemoveFurniture(location, _ => true) + + this.RemoveObjects(location, _ => true) + + this.RemoveTerrainFeatures(location, _ => true) + + this.RemoveLargeTerrainFeatures(location, p => everything || p is not Bush bush || bush.isDestroyable(location, p.currentTileLocation)) + + this.RemoveResourceClumps(location, _ => true); monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info); break; } @@ -165,11 +167,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { int removed = 0; - foreach (var pair in location.Objects.Pairs.ToArray()) + foreach ((Vector2 tile, SObject? obj) in location.Objects.Pairs.ToArray()) { - if (shouldRemove(pair.Value)) + if (shouldRemove(obj)) { - location.Objects.Remove(pair.Key); + location.Objects.Remove(tile); removed++; } } @@ -185,11 +187,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { int removed = 0; - foreach (var pair in location.terrainFeatures.Pairs.ToArray()) + foreach ((Vector2 tile, TerrainFeature? feature) in location.terrainFeatures.Pairs.ToArray()) { - if (shouldRemove(pair.Value)) + if (shouldRemove(feature)) { - location.terrainFeatures.Remove(pair.Key); + location.terrainFeatures.Remove(tile); removed++; } } @@ -225,7 +227,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { int removed = 0; - foreach (var clump in location.resourceClumps.Where(shouldRemove).ToArray()) + foreach (ResourceClump clump in location.resourceClumps.Where(shouldRemove).ToArray()) { location.resourceClumps.Remove(clump); removed++; diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/DownMineLevelCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/DownMineLevelCommand.cs index 0aa9c9c3..5b1a4a13 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/DownMineLevelCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/DownMineLevelCommand.cs @@ -1,9 +1,11 @@ +using System.Diagnostics.CodeAnalysis; using StardewValley; using StardewValley.Locations; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { /// <summary>A command which moves the player to the next mine level.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class DownMineLevelCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/HurryAllCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/HurryAllCommand.cs index 2deac5f8..09531720 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/HurryAllCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/HurryAllCommand.cs @@ -1,9 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { /// <summary>A command which immediately warps all NPCs to their scheduled positions. To hurry a single NPC, see <c>debug hurry npc-name</c> instead.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class HurryAllCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetDayCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetDayCommand.cs index 4028b3dc..399fd934 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetDayCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetDayCommand.cs @@ -1,10 +1,12 @@ -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using StardewModdingAPI.Utilities; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { /// <summary>A command which sets the current day.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetDayCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetMineLevelCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetMineLevelCommand.cs index 40f4b19f..f977fce3 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetMineLevelCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetMineLevelCommand.cs @@ -1,9 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { /// <summary>A command which moves the player to the given mine level.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetMineLevelCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs index a4cb35bb..505c0d1d 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using StardewModdingAPI.Utilities; using StardewValley; @@ -5,6 +6,7 @@ using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { /// <summary>A command which sets the current season.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetSeasonCommand : ConsoleCommand { /********* @@ -35,7 +37,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World } // parse arguments - if (!args.TryGet(0, "season", out string season, oneOf: this.ValidSeasons)) + if (!args.TryGet(0, "season", out string? season, oneOf: this.ValidSeasons)) return; // handle diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs index 2d4b4565..8c4458dd 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.Xna.Framework; using StardewValley; @@ -5,6 +6,7 @@ using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { /// <summary>A command which sets the current time.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetTimeCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetYearCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetYearCommand.cs index 95401962..a666a634 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetYearCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetYearCommand.cs @@ -1,10 +1,12 @@ -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using StardewModdingAPI.Utilities; using StardewValley; namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { /// <summary>A command which sets the current year.</summary> + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")] internal class SetYearCommand : ConsoleCommand { /********* diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemData/SearchableItem.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemData/SearchableItem.cs index 72d01eb7..3675a963 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemData/SearchableItem.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemData/SearchableItem.cs @@ -43,16 +43,6 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData this.Item = createItem(this); } - /// <summary>Construct an instance.</summary> - /// <param name="item">The item metadata to copy.</param> - public SearchableItem(SearchableItem item) - { - this.Type = item.Type; - this.ID = item.ID; - this.CreateItem = item.CreateItem; - this.Item = item.Item; - } - /// <summary>Get whether the item name contains a case-insensitive substring.</summary> /// <param name="substring">The substring to find.</param> public bool NameContains(string substring) 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 { |