encapsulate TrainerMod's argument parsing (#302)

This commit is contained in:
Jesse Plamondon-Willard 2017-07-02 02:45:02 -04:00
parent f9482906ae
commit 2ca49fba62
34 changed files with 290 additions and 282 deletions

View File

@ -0,0 +1,158 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using StardewModdingAPI;
namespace TrainerMod.Framework.Commands
{
/// <summary>Provides methods for parsing command-line arguments.</summary>
internal class ArgumentParser : IReadOnlyList<string>
{
/*********
** Properties
*********/
/// <summary>The command name for errors.</summary>
private readonly string CommandName;
/// <summary>The arguments to parse.</summary>
private readonly string[] Args;
/// <summary>Writes messages to the console and log file.</summary>
private readonly IMonitor Monitor;
/*********
** Accessors
*********/
/// <summary>Get the number of arguments.</summary>
public int Count => this.Args.Length;
/// <summary>Get the argument at the specified index in the list.</summary>
/// <param name="index">The zero-based index of the element to get.</param>
public string this[int index] => this.Args[index];
/// <summary>A method which parses a string argument into the given value.</summary>
/// <typeparam name="T">The expected argument type.</typeparam>
/// <param name="input">The argument to parse.</param>
/// <param name="output">The parsed value.</param>
/// <returns>Returns whether the argument was successfully parsed.</returns>
public delegate bool ParseDelegate<T>(string input, out T output);
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="commandName">The command name for errors.</param>
/// <param name="args">The arguments to parse.</param>
/// <param name="monitor">Writes messages to the console and log file.</param>
public ArgumentParser(string commandName, string[] args, IMonitor monitor)
{
this.CommandName = commandName;
this.Args = args;
this.Monitor = monitor;
}
/// <summary>Try to read a string argument.</summary>
/// <param name="index">The argument index.</param>
/// <param name="name">The argument name for error messages.</param>
/// <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.</param>
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;
}
/// <summary>Try to read an integer argument.</summary>
/// <param name="index">The argument index.</param>
/// <param name="name">The argument name for error messages.</param>
/// <param name="value">The parsed value.</param>
/// <param name="required">Whether to show an error if the argument is missing.</param>
/// <param name="min">The minimum value allowed.</param>
/// <param name="max">The maximum value allowed.</param>
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;
}
/// <summary>Returns an enumerator that iterates through the collection.</summary>
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
public IEnumerator<string> GetEnumerator()
{
return ((IEnumerable<string>)this.Args).GetEnumerator();
}
/// <summary>Returns an enumerator that iterates through a collection.</summary>
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
/*********
** Private methods
*********/
/// <summary>Log a usage error.</summary>
/// <param name="message">The message describing the error.</param>
private void LogError(string message)
{
this.Monitor.Log($"{message} Type 'help {this.CommandName}' for usage.", LogLevel.Error);
}
/// <summary>Print an error for an invalid int argument.</summary>
/// <param name="index">The argument index.</param>
/// <param name="name">The argument name for error messages.</param>
/// <param name="min">The minimum value allowed.</param>
/// <param name="max">The maximum value allowed.</param>
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.");
}
}
}

View File

@ -25,7 +25,7 @@ namespace TrainerMod.Framework.Commands
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
void Handle(IMonitor monitor, string command, string[] args);
void Handle(IMonitor monitor, string command, ArgumentParser args);
/// <summary>Perform any logic needed on update tick.</summary>
/// <param name="monitor">Writes messages to the console and log file.</param>

View File

@ -17,7 +17,7 @@ namespace TrainerMod.Framework.Commands.Other
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);

View File

@ -17,7 +17,7 @@ namespace TrainerMod.Framework.Commands.Other
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);

View File

@ -17,7 +17,7 @@ namespace TrainerMod.Framework.Commands.Other
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);

View File

@ -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
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);

View File

@ -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
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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 };

View File

@ -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
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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("<item> 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);

View File

@ -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
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);

View File

@ -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
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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<Dictionary<int, string>>("Data\\weapons").TryGetValue(weaponID, out string data))

View File

@ -22,9 +22,9 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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";

View File

@ -18,22 +18,23 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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;
}
}

View File

@ -32,9 +32,9 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);
}
}

View File

@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);
}
}
}

View File

@ -17,22 +17,16 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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;
}
}
}

View File

@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);
}
}
}

View File

@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);
}
}
}

View File

@ -32,7 +32,7 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);
}
}

View File

@ -17,29 +17,34 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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;
}
}

View File

@ -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
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);
}
}

View File

@ -32,7 +32,7 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);
}
}

View File

@ -17,22 +17,16 @@ namespace TrainerMod.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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;
}
}
}

View File

@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Saves
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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;

View File

@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.Saves
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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();

View File

@ -25,7 +25,7 @@ namespace TrainerMod.Framework.Commands
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
public abstract void Handle(IMonitor monitor, string command, string[] args);
public abstract void Handle(IMonitor monitor, string command, ArgumentParser args);
/// <summary>Perform any logic needed on update tick.</summary>
/// <param name="monitor">Writes messages to the console and log file.</param>
@ -47,26 +47,16 @@ namespace TrainerMod.Framework.Commands
/// <summary>Log an error indicating incorrect usage.</summary>
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="error">A sentence explaining the problem.</param>
/// <param name="command">The name of the command.</param>
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);
}
/// <summary>Log an error indicating a value must be an integer.</summary>
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The name of the command.</param>
protected void LogArgumentNotInt(IMonitor monitor, string command)
protected void LogArgumentNotInt(IMonitor monitor)
{
this.LogUsageError(monitor, "The value must be a whole number.", command);
}
/// <summary>Log an error indicating a value is invalid.</summary>
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The name of the command.</param>
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.");
}
}
}

View File

@ -18,7 +18,7 @@ namespace TrainerMod.Framework.Commands.World
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);

View File

@ -35,23 +35,18 @@ namespace TrainerMod.Framework.Commands.World
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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
{

View File

@ -18,24 +18,18 @@ namespace TrainerMod.Framework.Commands.World
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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;

View File

@ -1,5 +1,4 @@
using System;
using System.Linq;
using StardewModdingAPI;
using StardewValley;
@ -19,19 +18,11 @@ namespace TrainerMod.Framework.Commands.World
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);

View File

@ -25,22 +25,21 @@ namespace TrainerMod.Framework.Commands.World
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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);
}
}

View File

@ -18,24 +18,18 @@ namespace TrainerMod.Framework.Commands.World
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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;

View File

@ -18,24 +18,18 @@ namespace TrainerMod.Framework.Commands.World
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="command">The command name.</param>
/// <param name="args">The command arguments.</param>
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;

View File

@ -58,7 +58,8 @@ namespace TrainerMod
/// <param name="args">The command arguments.</param>
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);
}
/// <summary>Find all commands in the assembly.</summary>

View File

@ -51,6 +51,7 @@
<Compile Include="..\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Framework\Commands\ArgumentParser.cs" />
<Compile Include="Framework\Commands\Other\ShowDataFilesCommand.cs" />
<Compile Include="Framework\Commands\Other\ShowGameFilesCommand.cs" />
<Compile Include="Framework\Commands\Other\DebugCommand.cs" />