summaryrefslogtreecommitdiff
path: root/src/SMAPI.Mods.ConsoleCommands
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Mods.ConsoleCommands')
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs180
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/SMAPI.Mods.ConsoleCommands.csproj28
2 files changed, 159 insertions, 49 deletions
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs
index 0bebd2b7..6b3b27cd 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs
@@ -1,6 +1,11 @@
+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
{
@@ -8,20 +13,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
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)
+ // handle argument
+ if (farmType == "list")
+ this.HandleList(monitor);
+ else if (isVanillaId)
+ this.HandleVanillaFarmType(vanillaId, monitor);
+ else
+ this.HandleCustomFarmType(farmType, monitor);
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /****
+ ** 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)
+ {
+ 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
{
- monitor.Log($"Your current farm is already set to {farmType} ({SetFarmTypeCommand.GetFarmLabel(farmType)}).", LogLevel.Info);
+ 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(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);
+ 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;
+ }
- /*********
- ** Private methods
- *********/
- /// <summary>Change the farm type to the given value.</summary>
+ 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>
- private void SetFarmType(int type)
+ /// <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;
+ }
}
}
diff --git a/src/SMAPI.Mods.ConsoleCommands/SMAPI.Mods.ConsoleCommands.csproj b/src/SMAPI.Mods.ConsoleCommands/SMAPI.Mods.ConsoleCommands.csproj
index 528348a0..e3db8b47 100644
--- a/src/SMAPI.Mods.ConsoleCommands/SMAPI.Mods.ConsoleCommands.csproj
+++ b/src/SMAPI.Mods.ConsoleCommands/SMAPI.Mods.ConsoleCommands.csproj
@@ -2,7 +2,7 @@
<PropertyGroup>
<AssemblyName>ConsoleCommands</AssemblyName>
<RootNamespace>StardewModdingAPI.Mods.ConsoleCommands</RootNamespace>
- <TargetFramework>net452</TargetFramework>
+ <TargetFramework>net5.0</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
@@ -13,32 +13,12 @@
</ItemGroup>
<ItemGroup>
- <Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" />
+ <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>
- <!-- Windows only -->
- <ItemGroup Condition="'$(OS)' == 'Windows_NT'">
- <Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
- </ItemGroup>
-
- <!-- Game framework -->
- <Choose>
- <When Condition="$(DefineConstants.Contains(SMAPI_FOR_XNA))">
- <ItemGroup>
- <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
- <Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
- <Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
- <Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
- </ItemGroup>
- </When>
- <Otherwise>
- <ItemGroup>
- <Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
- </ItemGroup>
- </Otherwise>
- </Choose>
-
<ItemGroup>
<None Update="manifest.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>