diff --git a/src/TrainerMod/Framework/Commands/ArgumentParser.cs b/src/TrainerMod/Framework/Commands/ArgumentParser.cs
new file mode 100644
index 00000000..bce068f1
--- /dev/null
+++ b/src/TrainerMod/Framework/Commands/ArgumentParser.cs
@@ -0,0 +1,158 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using StardewModdingAPI;
+
+namespace TrainerMod.Framework.Commands
+{
+ /// Provides methods for parsing command-line arguments.
+ internal class ArgumentParser : IReadOnlyList
+ {
+ /*********
+ ** Properties
+ *********/
+ /// The command name for errors.
+ private readonly string CommandName;
+
+ /// The arguments to parse.
+ private readonly string[] Args;
+
+ /// Writes messages to the console and log file.
+ private readonly IMonitor Monitor;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// Get the number of arguments.
+ public int Count => this.Args.Length;
+
+ /// Get the argument at the specified index in the list.
+ /// The zero-based index of the element to get.
+ public string this[int index] => this.Args[index];
+
+ /// A method which parses a string argument into the given value.
+ /// The expected argument type.
+ /// The argument to parse.
+ /// The parsed value.
+ /// Returns whether the argument was successfully parsed.
+ public delegate bool ParseDelegate(string input, out T output);
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// Construct an instance.
+ /// The command name for errors.
+ /// The arguments to parse.
+ /// Writes messages to the console and log file.
+ public ArgumentParser(string commandName, string[] args, IMonitor monitor)
+ {
+ this.CommandName = commandName;
+ this.Args = args;
+ this.Monitor = monitor;
+ }
+
+ /// Try to read a string argument.
+ /// The argument index.
+ /// The argument name for error messages.
+ /// The parsed value.
+ /// Whether to show an error if the argument is missing.
+ /// Require that the argument match one of the given values.
+ public bool TryGet(int index, string name, out string value, bool required = true, string[] oneOf = null)
+ {
+ value = null;
+
+ // validate
+ if (this.Args.Length < index + 1)
+ {
+ if (required)
+ this.LogError($"Argument {index} ({name}) is required.");
+ return false;
+ }
+ if (oneOf?.Any() == true && !oneOf.Contains(this.Args[index]))
+ {
+ this.LogError($"Argument {index} ({name}) must be one of {string.Join(", ", oneOf)}.");
+ return false;
+ }
+
+ // get value
+ value = this.Args[index];
+ return true;
+ }
+
+ /// Try to read an integer argument.
+ /// The argument index.
+ /// The argument name for error messages.
+ /// The parsed value.
+ /// Whether to show an error if the argument is missing.
+ /// The minimum value allowed.
+ /// The maximum value allowed.
+ public bool TryGetInt(int index, string name, out int value, bool required = true, int? min = null, int? max = null)
+ {
+ value = 0;
+
+ // get argument
+ if (!this.TryGet(index, name, out string raw, required))
+ return false;
+
+ // parse
+ if (!int.TryParse(raw, out value))
+ {
+ this.LogIntFormatError(index, name, min, max);
+ return false;
+ }
+
+ // validate
+ if ((min.HasValue && value < min) || (max.HasValue && value > max))
+ {
+ this.LogIntFormatError(index, name, min, max);
+ return false;
+ }
+
+ return true;
+ }
+
+ /// Returns an enumerator that iterates through the collection.
+ /// An enumerator that can be used to iterate through the collection.
+ public IEnumerator GetEnumerator()
+ {
+ return ((IEnumerable)this.Args).GetEnumerator();
+ }
+
+ /// Returns an enumerator that iterates through a collection.
+ /// An object that can be used to iterate through the collection.
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// Log a usage error.
+ /// The message describing the error.
+ private void LogError(string message)
+ {
+ this.Monitor.Log($"{message} Type 'help {this.CommandName}' for usage.", LogLevel.Error);
+ }
+
+ /// Print an error for an invalid int argument.
+ /// The argument index.
+ /// The argument name for error messages.
+ /// The minimum value allowed.
+ /// The maximum value allowed.
+ private void LogIntFormatError(int index, string name, int? min, int? max)
+ {
+ if (min.HasValue && max.HasValue)
+ this.LogError($"Argument {index} ({name}) must be an integer between {min} and {max}.");
+ else if (min.HasValue)
+ this.LogError($"Argument {index} ({name}) must be an integer and at least {min}.");
+ else if (max.HasValue)
+ this.LogError($"Argument {index} ({name}) must be an integer and at most {max}.");
+ else
+ this.LogError($"Argument {index} ({name}) must be an integer.");
+ }
+ }
+}
diff --git a/src/TrainerMod/Framework/Commands/ITrainerCommand.cs b/src/TrainerMod/Framework/Commands/ITrainerCommand.cs
index 55f36ceb..3d97e799 100644
--- a/src/TrainerMod/Framework/Commands/ITrainerCommand.cs
+++ b/src/TrainerMod/Framework/Commands/ITrainerCommand.cs
@@ -25,7 +25,7 @@ namespace TrainerMod.Framework.Commands
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- void Handle(IMonitor monitor, string command, string[] args);
+ void Handle(IMonitor monitor, string command, ArgumentParser args);
/// Perform any logic needed on update tick.
/// Writes messages to the console and log file.
diff --git a/src/TrainerMod/Framework/Commands/Other/DebugCommand.cs b/src/TrainerMod/Framework/Commands/Other/DebugCommand.cs
index ad38d1ba..8c6e9f3b 100644
--- a/src/TrainerMod/Framework/Commands/Other/DebugCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Other/DebugCommand.cs
@@ -17,7 +17,7 @@ namespace TrainerMod.Framework.Commands.Other
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// submit command
string debugCommand = string.Join(" ", args);
diff --git a/src/TrainerMod/Framework/Commands/Other/ShowDataFilesCommand.cs b/src/TrainerMod/Framework/Commands/Other/ShowDataFilesCommand.cs
index b2985bb1..367a70c6 100644
--- a/src/TrainerMod/Framework/Commands/Other/ShowDataFilesCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Other/ShowDataFilesCommand.cs
@@ -17,7 +17,7 @@ namespace TrainerMod.Framework.Commands.Other
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
Process.Start(Constants.DataPath);
monitor.Log($"OK, opening {Constants.DataPath}.", LogLevel.Info);
diff --git a/src/TrainerMod/Framework/Commands/Other/ShowGameFilesCommand.cs b/src/TrainerMod/Framework/Commands/Other/ShowGameFilesCommand.cs
index 5695ce9a..67fa83a3 100644
--- a/src/TrainerMod/Framework/Commands/Other/ShowGameFilesCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Other/ShowGameFilesCommand.cs
@@ -17,7 +17,7 @@ namespace TrainerMod.Framework.Commands.Other
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
Process.Start(Constants.ExecutionPath);
monitor.Log($"OK, opening {Constants.ExecutionPath}.", LogLevel.Info);
diff --git a/src/TrainerMod/Framework/Commands/Player/AddFlooringCommand.cs b/src/TrainerMod/Framework/Commands/Player/AddFlooringCommand.cs
index 57bd39e3..1bc96466 100644
--- a/src/TrainerMod/Framework/Commands/Player/AddFlooringCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/AddFlooringCommand.cs
@@ -1,5 +1,4 @@
-using System.Linq;
-using StardewModdingAPI;
+using StardewModdingAPI;
using StardewValley;
using StardewValley.Objects;
@@ -19,24 +18,11 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
- if (!args.Any())
- {
- this.LogArgumentsInvalid(monitor, command);
+ // read arguments
+ if (!args.TryGetInt(0, "floor ID", out int floorID, min: 0, max: 39))
return;
- }
- if (!int.TryParse(args[0], out int floorID))
- {
- this.LogArgumentNotInt(monitor, command);
- return;
- }
- if (floorID < 0 || floorID > 39)
- {
- monitor.Log("There is no such flooring ID (must be between 0 and 39).", LogLevel.Error);
- return;
- }
// handle
Wallpaper wallpaper = new Wallpaper(floorID, isFloor: true);
diff --git a/src/TrainerMod/Framework/Commands/Player/AddItemCommand.cs b/src/TrainerMod/Framework/Commands/Player/AddItemCommand.cs
index 6d3cf968..190d040a 100644
--- a/src/TrainerMod/Framework/Commands/Player/AddItemCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/AddItemCommand.cs
@@ -1,5 +1,4 @@
-using System.Linq;
-using StardewModdingAPI;
+using StardewModdingAPI;
using StardewValley;
namespace TrainerMod.Framework.Commands.Player
@@ -18,39 +17,15 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
- if (!args.Any())
- {
- this.LogArgumentsInvalid(monitor, command);
+ // read arguments
+ if (!args.TryGetInt(0, "item ID", out int itemID, min: 0))
return;
- }
- if (!int.TryParse(args[0], out int itemID))
- {
- this.LogUsageError(monitor, "The item ID must be an integer.", command);
- return;
- }
-
- // parse arguments
- int count = 1;
- int quality = 0;
- if (args.Length > 1)
- {
- if (!int.TryParse(args[1], out count))
- {
- this.LogUsageError(monitor, "The optional count is invalid.", command);
- return;
- }
- }
- if (args.Length > 2)
- {
- if (!int.TryParse(args[2], out quality))
- {
- this.LogUsageError(monitor, "The optional quality is invalid.", command);
- return;
- }
- }
+ if (!args.TryGetInt(1, "count", out int count, min: 1, required: false))
+ count = 1;
+ if (!args.TryGetInt(2, "quality", out int quality, min: Object.lowQuality, max: Object.bestQuality, required: false))
+ quality = Object.lowQuality;
// spawn item
var item = new Object(itemID, count) { quality = quality };
diff --git a/src/TrainerMod/Framework/Commands/Player/AddRingCommand.cs b/src/TrainerMod/Framework/Commands/Player/AddRingCommand.cs
index d62d8b5b..93c5b2a5 100644
--- a/src/TrainerMod/Framework/Commands/Player/AddRingCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/AddRingCommand.cs
@@ -1,5 +1,4 @@
-using System.Linq;
-using StardewModdingAPI;
+using StardewModdingAPI;
using StardewValley;
using StardewValley.Objects;
@@ -19,24 +18,11 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
- if (!args.Any())
- {
- this.LogArgumentsInvalid(monitor, command);
+ // parse arguments
+ if (!args.TryGetInt(0, "ring ID", out int ringID, min: Ring.ringLowerIndexRange, max: Ring.ringUpperIndexRange))
return;
- }
- if (!int.TryParse(args[0], out int ringID))
- {
- monitor.Log("- is invalid", LogLevel.Error);
- return;
- }
- if (ringID < Ring.ringLowerIndexRange || ringID > Ring.ringUpperIndexRange)
- {
- monitor.Log($"There is no such ring ID (must be between {Ring.ringLowerIndexRange} and {Ring.ringUpperIndexRange}).", LogLevel.Error);
- return;
- }
// handle
Ring ring = new Ring(ringID);
diff --git a/src/TrainerMod/Framework/Commands/Player/AddWallpaperCommand.cs b/src/TrainerMod/Framework/Commands/Player/AddWallpaperCommand.cs
index e02b05a4..dddb9ffd 100644
--- a/src/TrainerMod/Framework/Commands/Player/AddWallpaperCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/AddWallpaperCommand.cs
@@ -1,5 +1,4 @@
-using System.Linq;
-using StardewModdingAPI;
+using StardewModdingAPI;
using StardewValley;
using StardewValley.Objects;
@@ -19,24 +18,11 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
- if (!args.Any())
- {
- this.LogArgumentsInvalid(monitor, command);
+ // parse arguments
+ if (!args.TryGetInt(0, "wallpaper ID", out int wallpaperID, min: 0, max: 111))
return;
- }
- if (!int.TryParse(args[0], out int wallpaperID))
- {
- this.LogArgumentNotInt(monitor, command);
- return;
- }
- if (wallpaperID < 0 || wallpaperID > 111)
- {
- monitor.Log("There is no such wallpaper ID (must be between 0 and 111).", LogLevel.Error);
- return;
- }
// handle
Wallpaper wallpaper = new Wallpaper(wallpaperID);
diff --git a/src/TrainerMod/Framework/Commands/Player/AddWeaponCommand.cs b/src/TrainerMod/Framework/Commands/Player/AddWeaponCommand.cs
index ee94093f..c4ea3d6f 100644
--- a/src/TrainerMod/Framework/Commands/Player/AddWeaponCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/AddWeaponCommand.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using System.Linq;
using StardewModdingAPI;
using StardewValley;
using StardewValley.Tools;
@@ -20,19 +19,11 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
- if (!args.Any())
- {
- this.LogArgumentsInvalid(monitor, command);
+ // parse arguments
+ if (!args.TryGetInt(0, "weapon ID", out int weaponID, min: 0))
return;
- }
- if (!int.TryParse(args[0], out int weaponID))
- {
- this.LogUsageError(monitor, "The weapon ID must be an integer.", command);
- return;
- }
// get raw weapon data
if (!Game1.content.Load>("Data\\weapons").TryGetValue(weaponID, out string data))
diff --git a/src/TrainerMod/Framework/Commands/Player/ListItemsCommand.cs b/src/TrainerMod/Framework/Commands/Player/ListItemsCommand.cs
index a1b9aceb..68adf8c2 100644
--- a/src/TrainerMod/Framework/Commands/Player/ListItemsCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/ListItemsCommand.cs
@@ -22,9 +22,9 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- var matches = this.GetItems(args).ToArray();
+ var matches = this.GetItems(args.ToArray()).ToArray();
// show matches
string summary = "Searching...\n";
diff --git a/src/TrainerMod/Framework/Commands/Player/SetColorCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetColorCommand.cs
index 00907fba..28ace0df 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetColorCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetColorCommand.cs
@@ -18,22 +18,23 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
- if (args.Length <= 2)
- {
- this.LogArgumentsInvalid(monitor, command);
+ // parse arguments
+ if (!args.TryGet(0, "target", out string target, oneOf: new[] { "hair", "eyes", "pants" }))
return;
- }
- if (!this.TryParseColor(args[1], out Color color))
+ if (!args.TryGet(1, "color", out string rawColor))
+ return;
+
+ // parse color
+ if (!this.TryParseColor(rawColor, out Color color))
{
- this.LogUsageError(monitor, "The color should be an RBG value like '255,150,0'.", command);
+ this.LogUsageError(monitor, "Argument 1 (color) must be an RBG value like '255,150,0'.");
return;
}
// handle
- switch (args[0])
+ switch (target)
{
case "hair":
Game1.player.hairstyleColor = color;
@@ -49,10 +50,6 @@ namespace TrainerMod.Framework.Commands.Player
Game1.player.pantsColor = color;
monitor.Log("OK, your pants color is updated.", LogLevel.Info);
break;
-
- default:
- this.LogArgumentsInvalid(monitor, command);
- break;
}
}
diff --git a/src/TrainerMod/Framework/Commands/Player/SetHealthCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetHealthCommand.cs
index d3f06459..f64e9035 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetHealthCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetHealthCommand.cs
@@ -32,9 +32,9 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
+ // no-argument mode
if (!args.Any())
{
monitor.Log($"You currently have {(this.InfiniteHealth ? "infinite" : Game1.player.health.ToString())} health. Specify a value to change it.", LogLevel.Info);
@@ -57,7 +57,7 @@ namespace TrainerMod.Framework.Commands.Player
monitor.Log($"OK, you now have {Game1.player.health} health.", LogLevel.Info);
}
else
- this.LogArgumentNotInt(monitor, command);
+ this.LogArgumentNotInt(monitor);
}
}
diff --git a/src/TrainerMod/Framework/Commands/Player/SetImmunityCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetImmunityCommand.cs
index ff74f981..59b28a3c 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetImmunityCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetImmunityCommand.cs
@@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// validate
if (!args.Any())
@@ -28,13 +28,11 @@ namespace TrainerMod.Framework.Commands.Player
}
// handle
- if (int.TryParse(args[0], out int amount))
+ if (args.TryGetInt(0, "amount", out int amount, min: 0))
{
Game1.player.immunity = amount;
monitor.Log($"OK, you now have {Game1.player.immunity} immunity.", LogLevel.Info);
}
- else
- this.LogArgumentNotInt(monitor, command);
}
}
}
diff --git a/src/TrainerMod/Framework/Commands/Player/SetLevelCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetLevelCommand.cs
index 4982a0b8..b223aa9f 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetLevelCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetLevelCommand.cs
@@ -17,22 +17,16 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// validate
- if (args.Length <= 2)
- {
- this.LogArgumentsInvalid(monitor, command);
+ if (!args.TryGet(0, "skill", out string skill, oneOf: new[] { "luck", "mining", "combat", "farming", "fishing", "foraging" }))
return;
- }
- if (!int.TryParse(args[1], out int level))
- {
- this.LogArgumentNotInt(monitor, command);
+ if (!args.TryGetInt(1, "level", out int level, min: 0, max: 10))
return;
- }
// handle
- switch (args[0])
+ switch (skill)
{
case "luck":
Game1.player.LuckLevel = level;
@@ -63,10 +57,6 @@ namespace TrainerMod.Framework.Commands.Player
Game1.player.ForagingLevel = level;
monitor.Log($"OK, your foraging skill is now {Game1.player.ForagingLevel}.", LogLevel.Info);
break;
-
- default:
- this.LogUsageError(monitor, "That isn't a valid skill.", command);
- break;
}
}
}
diff --git a/src/TrainerMod/Framework/Commands/Player/SetMaxHealthCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetMaxHealthCommand.cs
index 73ba252a..4b9d87dc 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetMaxHealthCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetMaxHealthCommand.cs
@@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// validate
if (!args.Any())
@@ -28,13 +28,11 @@ namespace TrainerMod.Framework.Commands.Player
}
// handle
- if (int.TryParse(args[0], out int maxHealth))
+ if (args.TryGetInt(0, "amount", out int amount, min: 1))
{
- Game1.player.maxHealth = maxHealth;
+ Game1.player.maxHealth = amount;
monitor.Log($"OK, you now have {Game1.player.maxHealth} max health.", LogLevel.Info);
}
- else
- this.LogArgumentNotInt(monitor, command);
}
}
}
diff --git a/src/TrainerMod/Framework/Commands/Player/SetMaxStaminaCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetMaxStaminaCommand.cs
index c21f6592..3997bb1b 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetMaxStaminaCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetMaxStaminaCommand.cs
@@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// validate
if (!args.Any())
@@ -28,13 +28,11 @@ namespace TrainerMod.Framework.Commands.Player
}
// handle
- if (int.TryParse(args[0], out int amount))
+ if (args.TryGetInt(0, "amount", out int amount, min: 1))
{
Game1.player.MaxStamina = amount;
monitor.Log($"OK, you now have {Game1.player.MaxStamina} max stamina.", LogLevel.Info);
}
- else
- this.LogArgumentNotInt(monitor, command);
}
}
}
diff --git a/src/TrainerMod/Framework/Commands/Player/SetMoneyCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetMoneyCommand.cs
index ad74499d..55e069a4 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetMoneyCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetMoneyCommand.cs
@@ -32,7 +32,7 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// validate
if (!args.Any())
@@ -57,7 +57,7 @@ namespace TrainerMod.Framework.Commands.Player
monitor.Log($"OK, you now have {Game1.player.Money} gold.", LogLevel.Info);
}
else
- this.LogArgumentNotInt(monitor, command);
+ this.LogArgumentNotInt(monitor);
}
}
diff --git a/src/TrainerMod/Framework/Commands/Player/SetNameCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetNameCommand.cs
index 8284d882..3fd4475c 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetNameCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetNameCommand.cs
@@ -17,29 +17,34 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
- if (args.Length <= 1)
- {
- monitor.Log($"Your name is currently '{Game1.player.Name}'. Type 'help player_setname' for usage.", LogLevel.Info);
+ // parse arguments
+ if (!args.TryGet(0, "target", out string target, oneOf: new[] { "player", "farm" }))
return;
- }
+ args.TryGet(1, "name", out string name, required: false);
// handle
- string target = args[0];
switch (target)
{
case "player":
- Game1.player.Name = args[1];
- monitor.Log($"OK, your player's name is now {Game1.player.Name}.", LogLevel.Info);
+ if (!string.IsNullOrWhiteSpace(name))
+ {
+ Game1.player.Name = args[1];
+ monitor.Log($"OK, your name is now {Game1.player.Name}.", LogLevel.Info);
+ }
+ else
+ monitor.Log($"Your name is currently '{Game1.player.Name}'. Type 'help player_setname' for usage.", LogLevel.Info);
break;
+
case "farm":
- Game1.player.farmName = args[1];
- monitor.Log($"OK, your farm's name is now {Game1.player.Name}.", LogLevel.Info);
- break;
- default:
- this.LogArgumentsInvalid(monitor, command);
+ if (!string.IsNullOrWhiteSpace(name))
+ {
+ Game1.player.farmName = args[1];
+ monitor.Log($"OK, your farm's name is now {Game1.player.farmName}.", LogLevel.Info);
+ }
+ else
+ monitor.Log($"Your farm's name is currently '{Game1.player.farmName}'. Type 'help player_setname' for usage.", LogLevel.Info);
break;
}
}
diff --git a/src/TrainerMod/Framework/Commands/Player/SetSpeedCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetSpeedCommand.cs
index a8c05d0c..40b87b62 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetSpeedCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetSpeedCommand.cs
@@ -1,5 +1,4 @@
-using System.Linq;
-using StardewModdingAPI;
+using StardewModdingAPI;
using StardewValley;
namespace TrainerMod.Framework.Commands.Player
@@ -18,22 +17,14 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
- if (!args.Any())
- {
- monitor.Log($"You currently have {Game1.player.addedSpeed} added speed. Specify a value to change it.", LogLevel.Info);
+ // parse arguments
+ if (!args.TryGetInt(0, "added speed", out int amount, min: 0))
return;
- }
- if (!int.TryParse(args[0], out int addedSpeed))
- {
- this.LogArgumentNotInt(monitor, command);
- return;
- }
// handle
- Game1.player.addedSpeed = addedSpeed;
+ Game1.player.addedSpeed = amount;
monitor.Log($"OK, your added speed is now {Game1.player.addedSpeed}.", LogLevel.Info);
}
}
diff --git a/src/TrainerMod/Framework/Commands/Player/SetStaminaCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetStaminaCommand.cs
index 55a55eab..d44d1370 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetStaminaCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetStaminaCommand.cs
@@ -32,7 +32,7 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// validate
if (!args.Any())
@@ -57,7 +57,7 @@ namespace TrainerMod.Framework.Commands.Player
monitor.Log($"OK, you now have {Game1.player.Stamina} stamina.", LogLevel.Info);
}
else
- this.LogArgumentNotInt(monitor, command);
+ this.LogArgumentNotInt(monitor);
}
}
diff --git a/src/TrainerMod/Framework/Commands/Player/SetStyleCommand.cs b/src/TrainerMod/Framework/Commands/Player/SetStyleCommand.cs
index 9ef5f88b..96e34af2 100644
--- a/src/TrainerMod/Framework/Commands/Player/SetStyleCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Player/SetStyleCommand.cs
@@ -17,22 +17,16 @@ namespace TrainerMod.Framework.Commands.Player
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
- if (args.Length <= 1)
- {
- this.LogArgumentsInvalid(monitor, command);
+ // parse arguments
+ if (!args.TryGet(0, "target", out string target, oneOf: new[] { "hair", "shirt", "acc", "skin", "shoe", "swim", "gender" }))
return;
- }
- if (!int.TryParse(args[1], out int styleID))
- {
- this.LogArgumentsInvalid(monitor, command);
+ if (!args.TryGetInt(1, "style ID", out int styleID))
return;
- }
// handle
- switch (args[0])
+ switch (target)
{
case "hair":
Game1.player.changeHairStyle(styleID);
@@ -71,7 +65,7 @@ namespace TrainerMod.Framework.Commands.Player
monitor.Log("OK, you're now in your swimming suit.", LogLevel.Info);
break;
default:
- this.LogUsageError(monitor, "The swim value should be 0 (no swimming suit) or 1 (swimming suit).", command);
+ this.LogUsageError(monitor, "The swim value should be 0 (no swimming suit) or 1 (swimming suit).");
break;
}
break;
@@ -88,14 +82,10 @@ namespace TrainerMod.Framework.Commands.Player
monitor.Log("OK, you're now female.", LogLevel.Info);
break;
default:
- this.LogUsageError(monitor, "The gender value should be 0 (male) or 1 (female).", command);
+ this.LogUsageError(monitor, "The gender value should be 0 (male) or 1 (female).");
break;
}
break;
-
- default:
- this.LogArgumentsInvalid(monitor, command);
- break;
}
}
}
diff --git a/src/TrainerMod/Framework/Commands/Saves/LoadCommand.cs b/src/TrainerMod/Framework/Commands/Saves/LoadCommand.cs
index 1a70b54c..121ad9a6 100644
--- a/src/TrainerMod/Framework/Commands/Saves/LoadCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Saves/LoadCommand.cs
@@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Saves
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
monitor.Log("Triggering load menu...", LogLevel.Info);
Game1.hasLoadedGame = false;
diff --git a/src/TrainerMod/Framework/Commands/Saves/SaveCommand.cs b/src/TrainerMod/Framework/Commands/Saves/SaveCommand.cs
index 8ce9738d..5f6941e9 100644
--- a/src/TrainerMod/Framework/Commands/Saves/SaveCommand.cs
+++ b/src/TrainerMod/Framework/Commands/Saves/SaveCommand.cs
@@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Saves
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
monitor.Log("Saving the game...", LogLevel.Info);
SaveGame.Save();
diff --git a/src/TrainerMod/Framework/Commands/TrainerCommand.cs b/src/TrainerMod/Framework/Commands/TrainerCommand.cs
index 1b18b44b..4715aa04 100644
--- a/src/TrainerMod/Framework/Commands/TrainerCommand.cs
+++ b/src/TrainerMod/Framework/Commands/TrainerCommand.cs
@@ -25,7 +25,7 @@ namespace TrainerMod.Framework.Commands
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public abstract void Handle(IMonitor monitor, string command, string[] args);
+ public abstract void Handle(IMonitor monitor, string command, ArgumentParser args);
/// Perform any logic needed on update tick.
/// Writes messages to the console and log file.
@@ -47,26 +47,16 @@ namespace TrainerMod.Framework.Commands
/// Log an error indicating incorrect usage.
/// Writes messages to the console and log file.
/// A sentence explaining the problem.
- /// The name of the command.
- protected void LogUsageError(IMonitor monitor, string error, string command)
+ protected void LogUsageError(IMonitor monitor, string error)
{
- monitor.Log($"{error} Type 'help {command}' for usage.", LogLevel.Error);
+ monitor.Log($"{error} Type 'help {this.Name}' for usage.", LogLevel.Error);
}
/// Log an error indicating a value must be an integer.
/// Writes messages to the console and log file.
- /// The name of the command.
- protected void LogArgumentNotInt(IMonitor monitor, string command)
+ protected void LogArgumentNotInt(IMonitor monitor)
{
- this.LogUsageError(monitor, "The value must be a whole number.", command);
- }
-
- /// Log an error indicating a value is invalid.
- /// Writes messages to the console and log file.
- /// The name of the command.
- protected void LogArgumentsInvalid(IMonitor monitor, string command)
- {
- this.LogUsageError(monitor, "The arguments are invalid.", command);
+ this.LogUsageError(monitor, "The value must be a whole number.");
}
}
}
diff --git a/src/TrainerMod/Framework/Commands/World/DownMineLevelCommand.cs b/src/TrainerMod/Framework/Commands/World/DownMineLevelCommand.cs
index 2700a0dc..4e62cf77 100644
--- a/src/TrainerMod/Framework/Commands/World/DownMineLevelCommand.cs
+++ b/src/TrainerMod/Framework/Commands/World/DownMineLevelCommand.cs
@@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.World
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
int level = (Game1.currentLocation as MineShaft)?.mineLevel ?? 0;
monitor.Log($"OK, warping you to mine level {level + 1}.", LogLevel.Info);
diff --git a/src/TrainerMod/Framework/Commands/World/FreezeTimeCommand.cs b/src/TrainerMod/Framework/Commands/World/FreezeTimeCommand.cs
index 89cd68cb..13d08398 100644
--- a/src/TrainerMod/Framework/Commands/World/FreezeTimeCommand.cs
+++ b/src/TrainerMod/Framework/Commands/World/FreezeTimeCommand.cs
@@ -35,23 +35,18 @@ namespace TrainerMod.Framework.Commands.World
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
if (args.Any())
{
- if (int.TryParse(args[0], out int value))
- {
- if (value == 0 || value == 1)
- {
- this.FreezeTime = value == 1;
- FreezeTimeCommand.FrozenTime = Game1.timeOfDay;
- monitor.Log($"OK, time is now {(this.FreezeTime ? "frozen" : "resumed")}.", LogLevel.Info);
- }
- else
- this.LogUsageError(monitor, "The value should be 0 (not frozen), 1 (frozen), or empty (toggle).", command);
- }
- else
- this.LogArgumentNotInt(monitor, command);
+ // parse arguments
+ if (!args.TryGetInt(0, "value", out int value, min: 0, max: 1))
+ return;
+
+ // handle
+ this.FreezeTime = value == 1;
+ FreezeTimeCommand.FrozenTime = Game1.timeOfDay;
+ monitor.Log($"OK, time is now {(this.FreezeTime ? "frozen" : "resumed")}.", LogLevel.Info);
}
else
{
diff --git a/src/TrainerMod/Framework/Commands/World/SetDayCommand.cs b/src/TrainerMod/Framework/Commands/World/SetDayCommand.cs
index e47b76a7..54267384 100644
--- a/src/TrainerMod/Framework/Commands/World/SetDayCommand.cs
+++ b/src/TrainerMod/Framework/Commands/World/SetDayCommand.cs
@@ -18,24 +18,18 @@ namespace TrainerMod.Framework.Commands.World
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
+ // no-argument mode
if (!args.Any())
{
monitor.Log($"The current date is {Game1.currentSeason} {Game1.dayOfMonth}. Specify a value to change the day.", LogLevel.Info);
return;
}
- if (!int.TryParse(args[0], out int day))
- {
- this.LogArgumentNotInt(monitor, command);
+
+ // parse arguments
+ if (!args.TryGetInt(0, "day", out int day, min: 1, max: 28))
return;
- }
- if (day > 28 || day <= 0)
- {
- this.LogUsageError(monitor, "That isn't a valid day.", command);
- return;
- }
// handle
Game1.dayOfMonth = day;
diff --git a/src/TrainerMod/Framework/Commands/World/SetMineLevelCommand.cs b/src/TrainerMod/Framework/Commands/World/SetMineLevelCommand.cs
index bfcc566f..225ec091 100644
--- a/src/TrainerMod/Framework/Commands/World/SetMineLevelCommand.cs
+++ b/src/TrainerMod/Framework/Commands/World/SetMineLevelCommand.cs
@@ -1,5 +1,4 @@
using System;
-using System.Linq;
using StardewModdingAPI;
using StardewValley;
@@ -19,19 +18,11 @@ namespace TrainerMod.Framework.Commands.World
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
- if (!args.Any())
- {
- this.LogArgumentsInvalid(monitor, command);
+ // parse arguments
+ if (!args.TryGetInt(0, "mine level", out int level, min: 1))
return;
- }
- if (!int.TryParse(args[0], out int level))
- {
- this.LogArgumentNotInt(monitor, command);
- return;
- }
// handle
level = Math.Max(1, level);
diff --git a/src/TrainerMod/Framework/Commands/World/SetSeasonCommand.cs b/src/TrainerMod/Framework/Commands/World/SetSeasonCommand.cs
index d60f8601..96c3d920 100644
--- a/src/TrainerMod/Framework/Commands/World/SetSeasonCommand.cs
+++ b/src/TrainerMod/Framework/Commands/World/SetSeasonCommand.cs
@@ -25,22 +25,21 @@ namespace TrainerMod.Framework.Commands.World
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
+ // no-argument mode
if (!args.Any())
{
monitor.Log($"The current season is {Game1.currentSeason}. Specify a value to change it.", LogLevel.Info);
return;
}
- if (!this.ValidSeasons.Contains(args[0]))
- {
- this.LogUsageError(monitor, "That isn't a valid season name.", command);
+
+ // parse arguments
+ if (!args.TryGet(0, "season", out string season, oneOf: this.ValidSeasons))
return;
- }
// handle
- Game1.currentSeason = args[0];
+ Game1.currentSeason = season;
monitor.Log($"OK, the date is now {Game1.currentSeason} {Game1.dayOfMonth}.", LogLevel.Info);
}
}
diff --git a/src/TrainerMod/Framework/Commands/World/SetTimeCommand.cs b/src/TrainerMod/Framework/Commands/World/SetTimeCommand.cs
index 4ecff485..c827ea5e 100644
--- a/src/TrainerMod/Framework/Commands/World/SetTimeCommand.cs
+++ b/src/TrainerMod/Framework/Commands/World/SetTimeCommand.cs
@@ -18,24 +18,18 @@ namespace TrainerMod.Framework.Commands.World
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
+ // no-argument mode
if (!args.Any())
{
monitor.Log($"The current time is {Game1.timeOfDay}. Specify a value to change it.", LogLevel.Info);
return;
}
- if (!int.TryParse(args[0], out int time))
- {
- this.LogArgumentNotInt(monitor, command);
+
+ // parse arguments
+ if (!args.TryGetInt(0, "time", out int time, min: 600, max: 2600))
return;
- }
- if (time > 2600 || time < 600)
- {
- this.LogUsageError(monitor, "That isn't a valid time.", command);
- return;
- }
// handle
Game1.timeOfDay = time;
diff --git a/src/TrainerMod/Framework/Commands/World/SetYearCommand.cs b/src/TrainerMod/Framework/Commands/World/SetYearCommand.cs
index 6b2b0d93..760fc170 100644
--- a/src/TrainerMod/Framework/Commands/World/SetYearCommand.cs
+++ b/src/TrainerMod/Framework/Commands/World/SetYearCommand.cs
@@ -18,24 +18,18 @@ namespace TrainerMod.Framework.Commands.World
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
- public override void Handle(IMonitor monitor, string command, string[] args)
+ public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
- // validate
+ // no-argument mode
if (!args.Any())
{
monitor.Log($"The current year is {Game1.year}. Specify a value to change the year.", LogLevel.Info);
return;
}
- if (!int.TryParse(args[0], out int year))
- {
- this.LogArgumentNotInt(monitor, command);
+
+ // parse arguments
+ if (!args.TryGetInt(0, "year", out int year, min: 1))
return;
- }
- if (year < 1)
- {
- this.LogUsageError(monitor, "That isn't a valid year.", command);
- return;
- }
// handle
Game1.year = year;
diff --git a/src/TrainerMod/TrainerMod.cs b/src/TrainerMod/TrainerMod.cs
index 047bbbfe..5db02cd6 100644
--- a/src/TrainerMod/TrainerMod.cs
+++ b/src/TrainerMod/TrainerMod.cs
@@ -58,7 +58,8 @@ namespace TrainerMod
/// The command arguments.
private void HandleCommand(ITrainerCommand command, string commandName, string[] args)
{
- command.Handle(this.Monitor, commandName, args);
+ ArgumentParser argParser = new ArgumentParser(commandName, args, this.Monitor);
+ command.Handle(this.Monitor, commandName, argParser);
}
/// Find all commands in the assembly.
diff --git a/src/TrainerMod/TrainerMod.csproj b/src/TrainerMod/TrainerMod.csproj
index 1702c577..ee17f970 100644
--- a/src/TrainerMod/TrainerMod.csproj
+++ b/src/TrainerMod/TrainerMod.csproj
@@ -51,6 +51,7 @@
Properties\GlobalAssemblyInfo.cs
+