diff options
author | Jesse Plamondon-Willard <github@jplamondonw.com> | 2016-12-07 20:36:28 -0500 |
---|---|---|
committer | Jesse Plamondon-Willard <github@jplamondonw.com> | 2016-12-07 20:36:28 -0500 |
commit | b019dd4f69c9fefeba9f14c2049fb352127e448f (patch) | |
tree | ab848b0e044a10c2b2a3dd358768a5ddeb4e0132 /src/TrainerMod | |
parent | f0433e5a41c01d73edcd20a6767b7979f636c0e6 (diff) | |
download | SMAPI-b019dd4f69c9fefeba9f14c2049fb352127e448f.tar.gz SMAPI-b019dd4f69c9fefeba9f14c2049fb352127e448f.tar.bz2 SMAPI-b019dd4f69c9fefeba9f14c2049fb352127e448f.zip |
replace out_items, out_melee, and out_rings commands with a searchable list_items command
Diffstat (limited to 'src/TrainerMod')
-rw-r--r-- | src/TrainerMod/ItemData/ISearchItem.cs | 21 | ||||
-rw-r--r-- | src/TrainerMod/ItemData/ItemType.cs | 15 | ||||
-rw-r--r-- | src/TrainerMod/ItemData/SearchableObject.cs | 48 | ||||
-rw-r--r-- | src/TrainerMod/ItemData/SearchableRing.cs | 48 | ||||
-rw-r--r-- | src/TrainerMod/ItemData/SearchableWeapon.cs | 48 | ||||
-rw-r--r-- | src/TrainerMod/TrainerMod.cs | 135 | ||||
-rw-r--r-- | src/TrainerMod/TrainerMod.csproj | 5 |
7 files changed, 278 insertions, 42 deletions
diff --git a/src/TrainerMod/ItemData/ISearchItem.cs b/src/TrainerMod/ItemData/ISearchItem.cs new file mode 100644 index 00000000..b2f7c2b8 --- /dev/null +++ b/src/TrainerMod/ItemData/ISearchItem.cs @@ -0,0 +1,21 @@ +namespace TrainerMod.ItemData +{ + /// <summary>An item that can be searched and added to the player's inventory through the console.</summary> + internal interface ISearchItem + { + /********* + ** Accessors + *********/ + /// <summary>Whether the item is valid.</summary> + bool IsValid { get; } + + /// <summary>The item ID.</summary> + int ID { get; } + + /// <summary>The item name.</summary> + string Name { get; } + + /// <summary>The item type.</summary> + ItemType Type { get; } + } +}
\ No newline at end of file diff --git a/src/TrainerMod/ItemData/ItemType.cs b/src/TrainerMod/ItemData/ItemType.cs new file mode 100644 index 00000000..2e049aa1 --- /dev/null +++ b/src/TrainerMod/ItemData/ItemType.cs @@ -0,0 +1,15 @@ +namespace TrainerMod.ItemData +{ + /// <summary>An item type that can be searched and added to the player through the console.</summary> + internal enum ItemType + { + /// <summary>Any object in <see cref="StardewValley.Game1.objectInformation"/> (except rings).</summary> + Object, + + /// <summary>A ring in <see cref="StardewValley.Game1.objectInformation"/>.</summary> + Ring, + + /// <summary>A weapon from <c>Data\weapons</c>.</summary> + Weapon + } +} diff --git a/src/TrainerMod/ItemData/SearchableObject.cs b/src/TrainerMod/ItemData/SearchableObject.cs new file mode 100644 index 00000000..30362f54 --- /dev/null +++ b/src/TrainerMod/ItemData/SearchableObject.cs @@ -0,0 +1,48 @@ +using StardewValley; + +namespace TrainerMod.ItemData +{ + /// <summary>An object that can be searched and added to the player's inventory through the console.</summary> + internal class SearchableObject : ISearchItem + { + /********* + ** Properties + *********/ + /// <summary>The underlying item.</summary> + private readonly Item Item; + + + /********* + ** Accessors + *********/ + /// <summary>Whether the item is valid.</summary> + public bool IsValid => this.Item != null && this.Item.Name != "Broken Item"; + + /// <summary>The item ID.</summary> + public int ID => this.Item.parentSheetIndex; + + /// <summary>The item name.</summary> + public string Name => this.Item.Name; + + /// <summary>The item type.</summary> + public ItemType Type => ItemType.Object; + + + /********* + ** Accessors + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="id">The item ID.</param> + public SearchableObject(int id) + { + try + { + this.Item = new Object(id, 1); + } + catch + { + // invalid + } + } + } +}
\ No newline at end of file diff --git a/src/TrainerMod/ItemData/SearchableRing.cs b/src/TrainerMod/ItemData/SearchableRing.cs new file mode 100644 index 00000000..7751e6aa --- /dev/null +++ b/src/TrainerMod/ItemData/SearchableRing.cs @@ -0,0 +1,48 @@ +using StardewValley.Objects; + +namespace TrainerMod.ItemData +{ + /// <summary>A ring that can be searched and added to the player's inventory through the console.</summary> + internal class SearchableRing : ISearchItem + { + /********* + ** Properties + *********/ + /// <summary>The underlying item.</summary> + private readonly Ring Ring; + + + /********* + ** Accessors + *********/ + /// <summary>Whether the item is valid.</summary> + public bool IsValid => this.Ring != null; + + /// <summary>The item ID.</summary> + public int ID => this.Ring.parentSheetIndex; + + /// <summary>The item name.</summary> + public string Name => this.Ring.Name; + + /// <summary>The item type.</summary> + public ItemType Type => ItemType.Ring; + + + /********* + ** Accessors + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="id">The ring ID.</param> + public SearchableRing(int id) + { + try + { + this.Ring = new Ring(id); + } + catch + { + // invalid + } + } + } +}
\ No newline at end of file diff --git a/src/TrainerMod/ItemData/SearchableWeapon.cs b/src/TrainerMod/ItemData/SearchableWeapon.cs new file mode 100644 index 00000000..cc9ef459 --- /dev/null +++ b/src/TrainerMod/ItemData/SearchableWeapon.cs @@ -0,0 +1,48 @@ +using StardewValley.Tools; + +namespace TrainerMod.ItemData +{ + /// <summary>A weapon that can be searched and added to the player's inventory through the console.</summary> + internal class SearchableWeapon : ISearchItem + { + /********* + ** Properties + *********/ + /// <summary>The underlying item.</summary> + private readonly MeleeWeapon Weapon; + + + /********* + ** Accessors + *********/ + /// <summary>Whether the item is valid.</summary> + public bool IsValid => this.Weapon != null; + + /// <summary>The item ID.</summary> + public int ID => this.Weapon.initialParentTileIndex; + + /// <summary>The item name.</summary> + public string Name => this.Weapon.Name; + + /// <summary>The item type.</summary> + public ItemType Type => ItemType.Weapon; + + + /********* + ** Accessors + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="id">The weapon ID.</param> + public SearchableWeapon(int id) + { + try + { + this.Weapon = new MeleeWeapon(id); + } + catch + { + // invalid + } + } + } +}
\ No newline at end of file diff --git a/src/TrainerMod/TrainerMod.cs b/src/TrainerMod/TrainerMod.cs index dda72564..9572c494 100644 --- a/src/TrainerMod/TrainerMod.cs +++ b/src/TrainerMod/TrainerMod.cs @@ -9,6 +9,7 @@ using StardewValley.Menus; using StardewValley.Objects; using StardewValley.Tools; using TrainerMod.Framework; +using TrainerMod.ItemData; using Object = StardewValley.Object; namespace TrainerMod @@ -99,9 +100,7 @@ namespace TrainerMod Command.RegisterCommand("player_addmelee", "Gives the player a melee item | player_addmelee <item>", new[] { "?<item>" }).CommandFired += this.HandlePlayerAddMelee; Command.RegisterCommand("player_addring", "Gives the player a ring | player_addring <item>", new[] { "?<item>" }).CommandFired += this.HandlePlayerAddRing; - Command.RegisterCommand("out_items", "Outputs a list of items | out_items", new[] { "" }).CommandFired += this.HandleOutItems; - Command.RegisterCommand("out_melee", "Outputs a list of melee weapons | out_melee", new[] { "" }).CommandFired += this.HandleOutMelee; - Command.RegisterCommand("out_rings", "Outputs a list of rings | out_rings", new[] { "" }).CommandFired += this.HandleOutRings; + Command.RegisterCommand("list_items", "Lists items in the game data | list_items [search]", new[] { "(String)<search>" }).CommandFired += this.HandleListItems; Command.RegisterCommand("world_settime", "Sets the time to the specified value | world_settime <value>", new[] { "(Int32)<value> The target time [06:00 AM is 600]" }).CommandFired += this.HandleWorldSetTime; Command.RegisterCommand("world_freezetime", "Freezes or thaws time | world_freezetime <value>", new[] { "(0 - 1)<value> Whether or not to freeze time. 0 is thawed, 1 is frozen" }).CommandFired += this.HandleWorldFreezeTime; @@ -657,49 +656,19 @@ namespace TrainerMod this.LogObjectValueNotSpecified(); } - /// <summary>The event raised when the 'out_items' command is triggered.</summary> + /// <summary>The event raised when the 'list_items' command is triggered.</summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments.</param> - private void HandleOutItems(object sender, EventArgsCommand e) + private void HandleListItems(object sender, EventArgsCommand e) { - for (var itemID = 0; itemID < 1000; itemID++) - { - try - { - Item itemName = new Object(itemID, 1); - if (itemName.Name != "Error Item") - this.Monitor.Log($"{itemID} | {itemName.Name}", LogLevel.Info); - } - catch { } - } - } - - /// <summary>The event raised when the 'out_melee' command is triggered.</summary> - /// <param name="sender">The event sender.</param> - /// <param name="e">The event arguments.</param> - private void HandleOutMelee(object sender, EventArgsCommand e) - { - var data = Game1.content.Load<Dictionary<int, string>>("Data\\weapons"); - this.Monitor.Log("DATA\\WEAPONS: ", LogLevel.Info); - foreach (var pair in data) - this.Monitor.Log($"{pair.Key} | {pair.Value}", LogLevel.Info); - } + var matches = this.GetItems(e.Command.CalledArgs).ToArray(); - /// <summary>The event raised when the 'out_rings' command is triggered.</summary> - /// <param name="sender">The event sender.</param> - /// <param name="e">The event arguments.</param> - private void HandleOutRings(object sender, EventArgsCommand e) - { - for (var ringID = 0; ringID < 100; ringID++) - { - try - { - Item item = new Ring(ringID); - if (item.Name != "Error Item") - this.Monitor.Log($"{ringID} | {item.Name}", LogLevel.Info); - } - catch { } - } + // show matches + string summary = "Searching...\n"; + if (matches.Any()) + this.Monitor.Log(summary + this.GetTableString(matches, new[] { "type", "id", "name" }, val => new[] { val.Type.ToString(), val.ID.ToString(), val.Name }), LogLevel.Info); + else + this.Monitor.Log(summary + "No items found", LogLevel.Info); } /// <summary>The event raised when the 'world_downMineLevel' command is triggered.</summary> @@ -725,6 +694,88 @@ namespace TrainerMod else this.LogValueNotSpecified(); } + + /**** + ** Helpers + ****/ + /// <summary>Get all items which can be searched and added to the player's inventory through the console.</summary> + /// <param name="searchWords">The search string to find.</param> + private IEnumerable<ISearchItem> GetItems(string[] searchWords) + { + // normalise search term + searchWords = searchWords?.Where(word => !string.IsNullOrWhiteSpace(word)).ToArray(); + if (searchWords?.Any() == false) + searchWords = null; + + // find matches + return ( + from item in this.GetItems() + let term = $"{item.ID}|{item.Type}|{item.Name}" + where searchWords == null || searchWords.All(word => term.IndexOf(word, StringComparison.CurrentCultureIgnoreCase) != -1) + select item + ); + } + + /// <summary>Get all items which can be searched and added to the player's inventory through the console.</summary> + private IEnumerable<ISearchItem> GetItems() + { + // objects + foreach (int id in Game1.objectInformation.Keys) + { + ISearchItem obj = id >= Ring.ringLowerIndexRange && id <= Ring.ringUpperIndexRange + ? new SearchableRing(id) + : (ISearchItem)new SearchableObject(id); + if (obj.IsValid) + yield return obj; + } + + // weapons + foreach (int id in Game1.content.Load<Dictionary<int, string>>("Data\\weapons").Keys) + { + ISearchItem weapon = new SearchableWeapon(id); + if (weapon.IsValid) + yield return weapon; + } + } + + /// <summary>Get an ASCII table for a set of tabular data.</summary> + /// <typeparam name="T">The data type.</typeparam> + /// <param name="data">The data to display.</param> + /// <param name="header">The table header.</param> + /// <param name="getRow">Returns a set of fields for a data value.</param> + private string GetTableString<T>(IEnumerable<T> data, string[] header, Func<T, string[]> getRow) + { + // get table data + int[] widths = header.Select(p => p.Length).ToArray(); + string[][] rows = data + .Select(item => + { + string[] fields = getRow(item); + if (fields.Length != widths.Length) + throw new InvalidOperationException($"Expected {widths.Length} columns, but found {fields.Length}: {string.Join(", ", fields)}"); + + for (int i = 0; i < fields.Length; i++) + widths[i] = Math.Max(widths[i], fields[i].Length); + + return fields; + }) + .ToArray(); + + // render fields + List<string[]> lines = new List<string[]>(rows.Length + 2) + { + header, + header.Select((value, i) => "".PadRight(widths[i], '-')).ToArray() + }; + lines.AddRange(rows); + + return string.Join( + Environment.NewLine, + lines.Select(line => string.Join(" | ", + line.Select((field, i) => field.PadRight(widths[i], ' ')).ToArray()) + ) + ); + } /**** ** Logging diff --git a/src/TrainerMod/TrainerMod.csproj b/src/TrainerMod/TrainerMod.csproj index 6d8b5f34..e262e135 100644 --- a/src/TrainerMod/TrainerMod.csproj +++ b/src/TrainerMod/TrainerMod.csproj @@ -102,6 +102,11 @@ <Link>Properties\GlobalAssemblyInfo.cs</Link> </Compile> <Compile Include="Framework\Extensions.cs" /> + <Compile Include="ItemData\ISearchItem.cs" /> + <Compile Include="ItemData\ItemType.cs" /> + <Compile Include="ItemData\SearchableObject.cs" /> + <Compile Include="ItemData\SearchableRing.cs" /> + <Compile Include="ItemData\SearchableWeapon.cs" /> <Compile Include="TrainerMod.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> |