From 7294cb3cc5aeed2849827b192c54db2059fe6a5f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 22 Dec 2018 16:08:52 -0500 Subject: [PATCH] add world_clear console command --- docs/release-notes.md | 1 + .../Framework/Commands/World/ClearCommand.cs | 246 ++++++++++++++++++ ...rdewModdingAPI.Mods.ConsoleCommands.csproj | 1 + 3 files changed, 248 insertions(+) create mode 100644 src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs diff --git a/docs/release-notes.md b/docs/release-notes.md index 41a6cd83..ca47a687 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,6 +1,7 @@ # Release notes ## Upcoming release * For players: + * Added `world_clear` console command to remove spawned or placed entities. * Tweaked installer to reduce antivirus false positives. ## 2.9.3 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 +{ + /// A command which clears in-game objects. + internal class ClearCommand : TrainerCommand + { + /********* + ** Properties + *********/ + /// The valid types that can be cleared. + private readonly string[] ValidTypes = { "debris", "fruit-trees", "grass", "trees", "everything" }; + + /// The resource clump IDs to consider debris. + private readonly int[] DebrisClumps = { ResourceClump.stumpIndex, ResourceClump.hollowLogIndex, ResourceClump.meteoriteIndex, ResourceClump.boulderIndex }; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + public ClearCommand() + : base( + name: "world_clear", + description: "Clears in-game entities in a given location.\n\n" + + "Usage: world_clear \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)." + ) + { } + + /// Handle the command. + /// Writes messages to the console and log file. + /// The command name. + /// The command arguments. + 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 + *********/ + /// Remove objects from a location matching a lambda. + /// The location to search. + /// Whether an entity should be removed. + /// Returns the number of removed entities. + private int RemoveObjects(GameLocation location, Func shouldRemove) + { + int removed = 0; + + foreach (var pair in location.Objects.Pairs.ToArray()) + { + if (shouldRemove(pair.Value)) + { + location.Objects.Remove(pair.Key); + removed++; + } + } + + return removed; + } + + /// Remove terrain features from a location matching a lambda. + /// The location to search. + /// Whether an entity should be removed. + /// Returns the number of removed entities. + private int RemoveTerrainFeatures(GameLocation location, Func shouldRemove) + { + int removed = 0; + + foreach (var pair in location.terrainFeatures.Pairs.ToArray()) + { + if (shouldRemove(pair.Value)) + { + location.terrainFeatures.Remove(pair.Key); + removed++; + } + } + + return removed; + } + + /// Remove large terrain features from a location matching a lambda. + /// The location to search. + /// Whether an entity should be removed. + /// Returns the number of removed entities. + private int RemoveLargeTerrainFeatures(GameLocation location, Func shouldRemove) + { + int removed = 0; + + foreach (LargeTerrainFeature feature in location.largeTerrainFeatures.ToArray()) + { + if (shouldRemove(feature)) + { + location.largeTerrainFeatures.Remove(feature); + removed++; + } + } + + return removed; + } + + /// Remove resource clumps from a location matching a lambda. + /// The location to search. + /// Whether an entity should be removed. + /// Returns the number of removed entities. + private int RemoveResourceClumps(GameLocation location, Func shouldRemove) + { + int removed = 0; + + // get resource clumps + IList resourceClumps = + (location as Farm)?.resourceClumps + ?? (IList)(location as Woods)?.stumps + ?? new List(); + + // remove matching clumps + foreach (var clump in resourceClumps.ToArray()) + { + if (shouldRemove(clump)) + { + resourceClumps.Remove(clump); + removed++; + } + } + + return removed; + } + + /// Remove furniture from a location matching a lambda. + /// The location to search. + /// Whether an entity should be removed. + /// Returns the number of removed entities. + private int RemoveFurniture(GameLocation location, Func 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; + } + } +} diff --git a/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj b/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj index d1f16e41..a3237a3d 100644 --- a/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj +++ b/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj @@ -62,6 +62,7 @@ +