From b019dd4f69c9fefeba9f14c2049fb352127e448f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 7 Dec 2016 20:36:28 -0500 Subject: replace out_items, out_melee, and out_rings commands with a searchable list_items command --- src/TrainerMod/ItemData/ISearchItem.cs | 21 +++++ src/TrainerMod/ItemData/ItemType.cs | 15 ++++ src/TrainerMod/ItemData/SearchableObject.cs | 48 ++++++++++ src/TrainerMod/ItemData/SearchableRing.cs | 48 ++++++++++ src/TrainerMod/ItemData/SearchableWeapon.cs | 48 ++++++++++ src/TrainerMod/TrainerMod.cs | 135 +++++++++++++++++++--------- src/TrainerMod/TrainerMod.csproj | 5 ++ 7 files changed, 278 insertions(+), 42 deletions(-) create mode 100644 src/TrainerMod/ItemData/ISearchItem.cs create mode 100644 src/TrainerMod/ItemData/ItemType.cs create mode 100644 src/TrainerMod/ItemData/SearchableObject.cs create mode 100644 src/TrainerMod/ItemData/SearchableRing.cs create mode 100644 src/TrainerMod/ItemData/SearchableWeapon.cs (limited to 'src/TrainerMod') 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 +{ + /// An item that can be searched and added to the player's inventory through the console. + internal interface ISearchItem + { + /********* + ** Accessors + *********/ + /// Whether the item is valid. + bool IsValid { get; } + + /// The item ID. + int ID { get; } + + /// The item name. + string Name { get; } + + /// The item type. + 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 +{ + /// An item type that can be searched and added to the player through the console. + internal enum ItemType + { + /// Any object in (except rings). + Object, + + /// A ring in . + Ring, + + /// A weapon from Data\weapons. + 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 +{ + /// An object that can be searched and added to the player's inventory through the console. + internal class SearchableObject : ISearchItem + { + /********* + ** Properties + *********/ + /// The underlying item. + private readonly Item Item; + + + /********* + ** Accessors + *********/ + /// Whether the item is valid. + public bool IsValid => this.Item != null && this.Item.Name != "Broken Item"; + + /// The item ID. + public int ID => this.Item.parentSheetIndex; + + /// The item name. + public string Name => this.Item.Name; + + /// The item type. + public ItemType Type => ItemType.Object; + + + /********* + ** Accessors + *********/ + /// Construct an instance. + /// The item ID. + 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 +{ + /// A ring that can be searched and added to the player's inventory through the console. + internal class SearchableRing : ISearchItem + { + /********* + ** Properties + *********/ + /// The underlying item. + private readonly Ring Ring; + + + /********* + ** Accessors + *********/ + /// Whether the item is valid. + public bool IsValid => this.Ring != null; + + /// The item ID. + public int ID => this.Ring.parentSheetIndex; + + /// The item name. + public string Name => this.Ring.Name; + + /// The item type. + public ItemType Type => ItemType.Ring; + + + /********* + ** Accessors + *********/ + /// Construct an instance. + /// The ring ID. + 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 +{ + /// A weapon that can be searched and added to the player's inventory through the console. + internal class SearchableWeapon : ISearchItem + { + /********* + ** Properties + *********/ + /// The underlying item. + private readonly MeleeWeapon Weapon; + + + /********* + ** Accessors + *********/ + /// Whether the item is valid. + public bool IsValid => this.Weapon != null; + + /// The item ID. + public int ID => this.Weapon.initialParentTileIndex; + + /// The item name. + public string Name => this.Weapon.Name; + + /// The item type. + public ItemType Type => ItemType.Weapon; + + + /********* + ** Accessors + *********/ + /// Construct an instance. + /// The weapon ID. + 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 ", new[] { "?" }).CommandFired += this.HandlePlayerAddMelee; Command.RegisterCommand("player_addring", "Gives the player a ring | player_addring ", new[] { "?" }).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)" }).CommandFired += this.HandleListItems; Command.RegisterCommand("world_settime", "Sets the time to the specified value | world_settime ", new[] { "(Int32) The target time [06:00 AM is 600]" }).CommandFired += this.HandleWorldSetTime; Command.RegisterCommand("world_freezetime", "Freezes or thaws time | world_freezetime ", new[] { "(0 - 1) Whether or not to freeze time. 0 is thawed, 1 is frozen" }).CommandFired += this.HandleWorldFreezeTime; @@ -657,49 +656,19 @@ namespace TrainerMod this.LogObjectValueNotSpecified(); } - /// The event raised when the 'out_items' command is triggered. + /// The event raised when the 'list_items' command is triggered. /// The event sender. /// The event arguments. - 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 { } - } - } - - /// The event raised when the 'out_melee' command is triggered. - /// The event sender. - /// The event arguments. - private void HandleOutMelee(object sender, EventArgsCommand e) - { - var data = Game1.content.Load>("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(); - /// The event raised when the 'out_rings' command is triggered. - /// The event sender. - /// The event arguments. - 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); } /// The event raised when the 'world_downMineLevel' command is triggered. @@ -725,6 +694,88 @@ namespace TrainerMod else this.LogValueNotSpecified(); } + + /**** + ** Helpers + ****/ + /// Get all items which can be searched and added to the player's inventory through the console. + /// The search string to find. + private IEnumerable 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 + ); + } + + /// Get all items which can be searched and added to the player's inventory through the console. + private IEnumerable 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>("Data\\weapons").Keys) + { + ISearchItem weapon = new SearchableWeapon(id); + if (weapon.IsValid) + yield return weapon; + } + } + + /// Get an ASCII table for a set of tabular data. + /// The data type. + /// The data to display. + /// The table header. + /// Returns a set of fields for a data value. + private string GetTableString(IEnumerable data, string[] header, Func 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 lines = new List(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 @@ Properties\GlobalAssemblyInfo.cs + + + + + -- cgit