summaryrefslogtreecommitdiff
path: root/src/TrainerMod
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2016-12-07 20:36:28 -0500
committerJesse Plamondon-Willard <github@jplamondonw.com>2016-12-07 20:36:28 -0500
commitb019dd4f69c9fefeba9f14c2049fb352127e448f (patch)
treeab848b0e044a10c2b2a3dd358768a5ddeb4e0132 /src/TrainerMod
parentf0433e5a41c01d73edcd20a6767b7979f636c0e6 (diff)
downloadSMAPI-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.cs21
-rw-r--r--src/TrainerMod/ItemData/ItemType.cs15
-rw-r--r--src/TrainerMod/ItemData/SearchableObject.cs48
-rw-r--r--src/TrainerMod/ItemData/SearchableRing.cs48
-rw-r--r--src/TrainerMod/ItemData/SearchableWeapon.cs48
-rw-r--r--src/TrainerMod/TrainerMod.cs135
-rw-r--r--src/TrainerMod/TrainerMod.csproj5
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>