summaryrefslogtreecommitdiff
path: root/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2018-12-22 16:08:52 -0500
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2018-12-22 16:08:52 -0500
commit7294cb3cc5aeed2849827b192c54db2059fe6a5f (patch)
treefa3e9dee464b06d434bd4d1a7c054975bb932d91 /src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World
parent270d436a176904ab39fc0ce97da2027dd6ac1114 (diff)
downloadSMAPI-7294cb3cc5aeed2849827b192c54db2059fe6a5f.tar.gz
SMAPI-7294cb3cc5aeed2849827b192c54db2059fe6a5f.tar.bz2
SMAPI-7294cb3cc5aeed2849827b192c54db2059fe6a5f.zip
add world_clear console command
Diffstat (limited to 'src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World')
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs
new file mode 100644
index 00000000..9b5f07de
--- /dev/null
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs
@@ -0,0 +1,246 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using StardewValley;
+using StardewValley.Locations;
+using StardewValley.Objects;
+using StardewValley.TerrainFeatures;
+using SObject = StardewValley.Object;
+
+namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
+{
+ /// <summary>A command which clears in-game objects.</summary>
+ internal class ClearCommand : TrainerCommand
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>The valid types that can be cleared.</summary>
+ private readonly string[] ValidTypes = { "debris", "fruit-trees", "grass", "trees", "everything" };
+
+ /// <summary>The resource clump IDs to consider debris.</summary>
+ private readonly int[] DebrisClumps = { ResourceClump.stumpIndex, ResourceClump.hollowLogIndex, ResourceClump.meteoriteIndex, ResourceClump.boulderIndex };
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ public ClearCommand()
+ : base(
+ name: "world_clear",
+ description: "Clears in-game entities in a given location.\n\n"
+ + "Usage: world_clear <location> <object type>\n"
+ + "- location: the location name for which to clear objects (like Farm), or 'current' for the current location.\n"
+ + " - object type: the type of object clear. You can specify 'debris' (stones/twigs/weeds and dead crops), 'grass', and 'trees' / 'fruit-trees'. You can also specify 'everything', which includes things not removed by the other types (like furniture or resource clumps)."
+ )
+ { }
+
+ /// <summary>Handle the command.</summary>
+ /// <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, ArgumentParser args)
+ {
+ // check context
+ if (!Context.IsWorldReady)
+ {
+ monitor.Log("You need to load a save to use this command.", LogLevel.Error);
+ return;
+ }
+
+ // parse arguments
+ if (!args.TryGet(0, "location", out string locationName, required: true))
+ return;
+ if (!args.TryGet(1, "object type", out string type, required: true, oneOf: this.ValidTypes))
+ return;
+
+ // get target location
+ GameLocation location = Game1.locations.FirstOrDefault(p => p.Name != null && p.Name.Equals(locationName, StringComparison.InvariantCultureIgnoreCase));
+ if (location == null && locationName == "current")
+ location = Game1.currentLocation;
+ if (location == null)
+ {
+ string[] locationNames = (from loc in Game1.locations where !string.IsNullOrWhiteSpace(loc.Name) orderby loc.Name select loc.Name).ToArray();
+ monitor.Log($"Could not find a location with that name. Must be one of [{string.Join(", ", locationNames)}].", LogLevel.Error);
+ return;
+ }
+
+ // apply
+ switch (type)
+ {
+ case "debris":
+ {
+ int removed = 0;
+ foreach (var pair in location.terrainFeatures.Pairs.ToArray())
+ {
+ TerrainFeature feature = pair.Value;
+ if (feature is HoeDirt dirt && dirt.crop?.dead == true)
+ {
+ dirt.crop = null;
+ removed++;
+ }
+ }
+
+ removed +=
+ this.RemoveObjects(location, obj => obj.Name.ToLower().Contains("weed") || obj.Name == "Twig" || obj.Name == "Stone")
+ + this.RemoveResourceClumps(location, clump => this.DebrisClumps.Contains(clump.parentSheetIndex.Value));
+
+ monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
+ break;
+ }
+
+ case "fruit-trees":
+ {
+ int removed = this.RemoveTerrainFeatures(location, feature => feature is FruitTree);
+ monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
+ break;
+ }
+
+ case "grass":
+ {
+ int removed = this.RemoveTerrainFeatures(location, feature => feature is Grass);
+ monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
+ break;
+ }
+
+ case "trees":
+ {
+ int removed = this.RemoveTerrainFeatures(location, feature => feature is Tree);
+ monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
+ break;
+ }
+
+ case "everything":
+ {
+ int removed =
+ this.RemoveFurniture(location, p => true)
+ + this.RemoveObjects(location, p => true)
+ + this.RemoveTerrainFeatures(location, p => true)
+ + this.RemoveLargeTerrainFeatures(location, p => true)
+ + this.RemoveResourceClumps(location, p => true);
+ monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
+ break;
+ }
+
+ default:
+ monitor.Log($"Unknown type '{type}'. Must be one [{string.Join(", ", this.ValidTypes)}].", LogLevel.Error);
+ break;
+ }
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Remove objects from a location matching a lambda.</summary>
+ /// <param name="location">The location to search.</param>
+ /// <param name="shouldRemove">Whether an entity should be removed.</param>
+ /// <returns>Returns the number of removed entities.</returns>
+ private int RemoveObjects(GameLocation location, Func<SObject, bool> shouldRemove)
+ {
+ int removed = 0;
+
+ foreach (var pair in location.Objects.Pairs.ToArray())
+ {
+ if (shouldRemove(pair.Value))
+ {
+ location.Objects.Remove(pair.Key);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ /// <summary>Remove terrain features from a location matching a lambda.</summary>
+ /// <param name="location">The location to search.</param>
+ /// <param name="shouldRemove">Whether an entity should be removed.</param>
+ /// <returns>Returns the number of removed entities.</returns>
+ private int RemoveTerrainFeatures(GameLocation location, Func<TerrainFeature, bool> shouldRemove)
+ {
+ int removed = 0;
+
+ foreach (var pair in location.terrainFeatures.Pairs.ToArray())
+ {
+ if (shouldRemove(pair.Value))
+ {
+ location.terrainFeatures.Remove(pair.Key);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ /// <summary>Remove large terrain features from a location matching a lambda.</summary>
+ /// <param name="location">The location to search.</param>
+ /// <param name="shouldRemove">Whether an entity should be removed.</param>
+ /// <returns>Returns the number of removed entities.</returns>
+ private int RemoveLargeTerrainFeatures(GameLocation location, Func<LargeTerrainFeature, bool> shouldRemove)
+ {
+ int removed = 0;
+
+ foreach (LargeTerrainFeature feature in location.largeTerrainFeatures.ToArray())
+ {
+ if (shouldRemove(feature))
+ {
+ location.largeTerrainFeatures.Remove(feature);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ /// <summary>Remove resource clumps from a location matching a lambda.</summary>
+ /// <param name="location">The location to search.</param>
+ /// <param name="shouldRemove">Whether an entity should be removed.</param>
+ /// <returns>Returns the number of removed entities.</returns>
+ private int RemoveResourceClumps(GameLocation location, Func<ResourceClump, bool> shouldRemove)
+ {
+ int removed = 0;
+
+ // get resource clumps
+ IList<ResourceClump> resourceClumps =
+ (location as Farm)?.resourceClumps
+ ?? (IList<ResourceClump>)(location as Woods)?.stumps
+ ?? new List<ResourceClump>();
+
+ // remove matching clumps
+ foreach (var clump in resourceClumps.ToArray())
+ {
+ if (shouldRemove(clump))
+ {
+ resourceClumps.Remove(clump);
+ removed++;
+ }
+ }
+
+ return removed;
+ }
+
+ /// <summary>Remove furniture from a location matching a lambda.</summary>
+ /// <param name="location">The location to search.</param>
+ /// <param name="shouldRemove">Whether an entity should be removed.</param>
+ /// <returns>Returns the number of removed entities.</returns>
+ private int RemoveFurniture(GameLocation location, Func<Furniture, bool> shouldRemove)
+ {
+ int removed = 0;
+
+ if (location is DecoratableLocation decoratableLocation)
+ {
+ foreach (Furniture furniture in decoratableLocation.furniture.ToArray())
+ {
+ if (shouldRemove(furniture))
+ {
+ decoratableLocation.furniture.Remove(furniture);
+ removed++;
+ }
+ }
+ }
+
+ return removed;
+ }
+ }
+}