update 'set_farm_type' console command for custom farm types

This commit is contained in:
Jesse Plamondon-Willard 2021-10-30 21:23:28 -04:00
parent fe675540b5
commit f87e65caf5
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
2 changed files with 159 additions and 28 deletions

View File

@ -1,27 +1,23 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using StardewValley;
using StardewValley.GameData;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which changes the player's farm type.</summary>
internal class SetFarmTypeCommand : ConsoleCommand
{
/*********
** Fields
*********/
/// <summary>The vanilla farm type IDs.</summary>
private static readonly ISet<int> VanillaFarmTypes = new HashSet<int>(
Enumerable.Range(0, Farm.layout_max + 1)
);
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
public SetFarmTypeCommand()
: base("set_farm_type", $"Sets the current player's farm type.\n\nUsage: set_farm_type <farm type>\n- farm type: one of {string.Join(", ", SetFarmTypeCommand.VanillaFarmTypes.Select(id => $"{id} ({SetFarmTypeCommand.GetFarmLabel(id)})"))}.") { }
: base("set_farm_type", "Sets the current player's farm type.\n\nUsage: set_farm_type <farm type>\n- farm type: the farm type to set. Enter `set_farm_type list` for a list of available farm types.") { }
/// <summary>Handle the command.</summary>
/// <param name="monitor">Writes messages to the console and log file.</param>
@ -29,47 +25,141 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
/// <param name="args">The command arguments.</param>
public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// validation checks
// validate
if (!Context.IsWorldReady)
{
monitor.Log("You must load a save to use this command.", LogLevel.Error);
return;
}
// parse argument
if (!args.TryGetInt(0, "farm type", out int farmType, min: 0, max: Farm.layout_max))
// parse arguments
if (!args.TryGet(0, "farm type", out string farmType))
return;
bool isVanillaId = int.TryParse(farmType, out int vanillaId) && vanillaId is (>= 0 and < Farm.layout_max);
// handle
if (Game1.whichFarm == farmType)
{
monitor.Log($"Your current farm is already set to {farmType} ({SetFarmTypeCommand.GetFarmLabel(farmType)}).", LogLevel.Info);
return;
}
this.SetFarmType(farmType);
monitor.Log($"Your current farm has been converted to {farmType} ({SetFarmTypeCommand.GetFarmLabel(farmType)}).", LogLevel.Warn);
monitor.Log("Saving and reloading is recommended to make sure everything is updated for the change.", LogLevel.Warn);
// handle argument
if (farmType == "list")
this.HandleList(monitor);
else if (isVanillaId)
this.HandleVanillaFarmType(vanillaId, monitor);
else
this.HandleCustomFarmType(farmType, monitor);
}
/*********
** Private methods
*********/
/// <summary>Change the farm type to the given value.</summary>
/// <param name="type">The farm type ID.</param>
private void SetFarmType(int type)
/****
** Handlers
****/
/// <summary>Print a list of available farm types.</summary>
/// <param name="monitor">Writes messages to the console and log file.</param>
private void HandleList(IMonitor monitor)
{
Game1.whichFarm = type;
StringBuilder result = new();
// list vanilla types
result.AppendLine("The farm type can be one of these vanilla types:");
foreach (var type in this.GetVanillaFarmTypes())
result.AppendLine($" - {type.Key} ({type.Value})");
result.AppendLine();
// list custom types
{
var customTypes = this.GetCustomFarmTypes();
if (customTypes.Any())
{
result.AppendLine("Or one of these custom farm types:");
foreach (var type in customTypes.Values.OrderBy(p => p.ID))
result.AppendLine($" - {type.ID} ({this.GetCustomName(type)})");
}
else
result.AppendLine("Or a custom farm type (though none is loaded currently).");
}
// print
monitor.Log(result.ToString(), LogLevel.Info);
}
/// <summary>Set a vanilla farm type.</summary>
/// <param name="type">The farm type.</param>
/// <param name="monitor">Writes messages to the console and log file.</param>
private void HandleVanillaFarmType(int type, IMonitor monitor)
{
if (Game1.whichFarm == type)
{
monitor.Log($"Your current farm is already set to {type} ({this.GetVanillaName(type)}).", LogLevel.Info);
return;
}
this.SetFarmType(type, null);
this.PrintSuccess(monitor, $"{type} ({this.GetVanillaName(type)}");
}
/// <summary>Set a custom farm type.</summary>
/// <param name="id">The farm type ID.</param>
/// <param name="monitor">Writes messages to the console and log file.</param>
private void HandleCustomFarmType(string id, IMonitor monitor)
{
if (Game1.whichModFarm?.ID == id)
{
monitor.Log($"Your current farm is already set to {id} ({this.GetCustomName(Game1.whichModFarm)}).", LogLevel.Info);
return;
}
if (!this.GetCustomFarmTypes().TryGetValue(id, out ModFarmType customFarmType))
{
monitor.Log($"Invalid farm type '{id}'. Enter `help set_farm_type` for more info.", LogLevel.Error);
return;
}
this.SetFarmType(Farm.mod_layout, customFarmType);
this.PrintSuccess(monitor, $"{id} ({this.GetCustomName(customFarmType)})");
}
/// <summary>Change the farm type.</summary>
/// <param name="type">The farm type ID.</param>
/// <param name="customFarmData">The custom farm type data, if applicable.</param>
private void SetFarmType(int type, ModFarmType customFarmData)
{
// set flags
Game1.whichFarm = type;
Game1.whichModFarm = customFarmData;
// update farm map
Farm farm = Game1.getFarm();
farm.mapPath.Value = $@"Maps\{Farm.getMapNameFromTypeInt(Game1.whichFarm)}";
farm.reloadMap();
// clear spouse area cache to avoid errors
FieldInfo cacheField = farm.GetType().GetField("_baseSpouseAreaTiles", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (cacheField == null)
throw new InvalidOperationException("Failed to access '_baseSpouseAreaTiles' field to clear spouse area cache.");
if (cacheField.GetValue(farm) is not IDictionary cache)
throw new InvalidOperationException($"The farm's '_baseSpouseAreaTiles' field didn't match the expected {nameof(IDictionary)} type.");
cache.Clear();
}
private void PrintSuccess(IMonitor monitor, string label)
{
StringBuilder result = new();
result.AppendLine($"Your current farm has been converted to {label}. Saving and reloading is recommended to make sure everything is updated for the change.");
result.AppendLine();
result.AppendLine("This doesn't move items that are out of bounds on the new map. If you need to clean up, you can...");
result.AppendLine(" - temporarily switch back to the previous farm type;");
result.AppendLine(" - or use a mod like Noclip Mode: https://www.nexusmods.com/stardewvalley/mods/3900 ;");
result.AppendLine(" - or use the world_clear console command (enter `help world_clear` for details).");
monitor.Log(result.ToString(), LogLevel.Warn);
}
/****
** Vanilla farm types
****/
/// <summary>Get the display name for a vanilla farm type.</summary>
/// <param name="type">The farm type.</param>
private static string GetFarmLabel(int type)
private string GetVanillaName(int type)
{
string translationKey = type switch
{
@ -87,5 +177,45 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
? Game1.content.LoadString(@$"Strings\UI:{translationKey}").Split('_')[0]
: type.ToString();
}
/// <summary>Get the available vanilla farm types by ID.</summary>
private IDictionary<int, string> GetVanillaFarmTypes()
{
IDictionary<int, string> farmTypes = new Dictionary<int, string>();
foreach (int id in Enumerable.Range(0, Farm.layout_max))
farmTypes[id] = this.GetVanillaName(id);
return farmTypes;
}
/****
** Custom farm types
****/
/// <summary>Get the display name for a custom farm type.</summary>
/// <param name="farmType">The custom farm type.</param>
private string GetCustomName(ModFarmType farmType)
{
if (string.IsNullOrWhiteSpace(farmType?.TooltipStringPath))
return farmType?.ID;
return Game1.content.LoadString(farmType.TooltipStringPath)?.Split('_')[0] ?? farmType.ID;
}
/// <summary>Get the available custom farm types by ID.</summary>
private IDictionary<string, ModFarmType> GetCustomFarmTypes()
{
IDictionary<string, ModFarmType> farmTypes = new Dictionary<string, ModFarmType>(StringComparer.OrdinalIgnoreCase);
foreach (ModFarmType farmType in Game1.content.Load<List<ModFarmType>>("Data\\AdditionalFarms"))
{
if (string.IsNullOrWhiteSpace(farmType.ID))
continue;
farmTypes[farmType.ID] = farmType;
}
return farmTypes;
}
}
}

View File

@ -16,6 +16,7 @@
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
<Reference Include="Stardew Valley" HintPath="$(GamePath)\Stardew Valley.dll" Private="False" />
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="False" />
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="False" />
</ItemGroup>
<ItemGroup>