/* * WorldEdit, a Minecraft world manipulation toolkit * Copyright (C) sk89q * Copyright (C) WorldEdit team and contributors * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ package com.sk89q.worldedit.command; import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL; import static com.sk89q.worldedit.command.util.Logging.LogMode.ORIENTATION_REGION; import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; import static com.sk89q.worldedit.regions.Regions.asFlatRegion; import static com.sk89q.worldedit.regions.Regions.maximumBlockY; import static com.sk89q.worldedit.regions.Regions.minimumBlockY; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.function.GroundFunction; import com.sk89q.worldedit.function.generator.FloraGenerator; import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.NoiseFilter2D; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.LayerVisitor; import com.sk89q.worldedit.internal.annotation.Direction; import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.convolution.GaussianKernel; import com.sk89q.worldedit.math.convolution.HeightMap; import com.sk89q.worldedit.math.convolution.HeightMapFilter; import com.sk89q.worldedit.math.noise.RandomNoise; import com.sk89q.worldedit.regions.ConvexPolyhedralRegion; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.util.command.binding.Range; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.binding.Text; import com.sk89q.worldedit.util.command.parametric.Optional; import java.util.ArrayList; import java.util.List; /** * Commands that operate on regions. */ public class RegionCommands { private final WorldEdit worldEdit; /** * Create a new instance. * * @param worldEdit reference to WorldEdit */ public RegionCommands(WorldEdit worldEdit) { checkNotNull(worldEdit); this.worldEdit = worldEdit; } @Command( aliases = { "/line" }, usage = " [thickness]", desc = "Draws a line segment between cuboid selection corners", help = "Draws a line segment between cuboid selection corners.\n" + "Can only be used with cuboid selections.\n" + "Flags:\n" + " -h generates only a shell", flags = "h", min = 1, max = 2 ) @CommandPermissions("worldedit.region.line") @Logging(REGION) public void line(Player player, EditSession editSession, @Selection Region region, Pattern pattern, @Optional("0") @Range(min = 0) int thickness, @Switch('h') boolean shell) throws WorldEditException { if (!(region instanceof CuboidRegion)) { player.printError("//line only works with cuboid selections"); return; } CuboidRegion cuboidregion = (CuboidRegion) region; BlockVector3 pos1 = cuboidregion.getPos1(); BlockVector3 pos2 = cuboidregion.getPos2(); int blocksChanged = editSession.drawLine(pattern, pos1, pos2, thickness, !shell); player.print(blocksChanged + " block(s) have been changed."); } @Command( aliases = { "/curve" }, usage = " [thickness]", desc = "Draws a spline through selected points", help = "Draws a spline through selected points.\n" + "Can only be used with convex polyhedral selections.\n" + "Flags:\n" + " -h generates only a shell", flags = "h", min = 1, max = 2 ) @CommandPermissions("worldedit.region.curve") @Logging(REGION) public void curve(Player player, EditSession editSession, @Selection Region region, Pattern pattern, @Optional("0") @Range(min = 0) int thickness, @Switch('h') boolean shell) throws WorldEditException { if (!(region instanceof ConvexPolyhedralRegion)) { player.printError("//curve only works with convex polyhedral selections"); return; } ConvexPolyhedralRegion cpregion = (ConvexPolyhedralRegion) region; List vectors = new ArrayList<>(cpregion.getVertices()); int blocksChanged = editSession.drawSpline(pattern, vectors, 0, 0, 0, 10, thickness, !shell); player.print(blocksChanged + " block(s) have been changed."); } @Command( aliases = { "/replace", "/re", "/rep" }, usage = "[from-block] ", desc = "Replace all blocks in the selection with another", flags = "f", min = 1, max = 2 ) @CommandPermissions("worldedit.region.replace") @Logging(REGION) public void replace(Player player, EditSession editSession, @Selection Region region, @Optional Mask from, Pattern to) throws WorldEditException { if (from == null) { from = new ExistingBlockMask(editSession); } int affected = editSession.replaceBlocks(region, from, to); player.print(affected + " block(s) have been replaced."); } @Command( aliases = { "/overlay" }, usage = "", desc = "Set a block on top of blocks in the region", min = 1, max = 1 ) @CommandPermissions("worldedit.region.overlay") @Logging(REGION) public void overlay(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { int affected = editSession.overlayCuboidBlocks(region, pattern); player.print(affected + " block(s) have been overlaid."); } @Command( aliases = { "/center", "/middle" }, usage = "", desc = "Set the center block(s)", min = 1, max = 1 ) @Logging(REGION) @CommandPermissions("worldedit.region.center") public void center(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { int affected = editSession.center(region, pattern); player.print("Center set ("+ affected + " blocks changed)"); } @Command( aliases = { "/naturalize" }, usage = "", desc = "3 layers of dirt on top then rock below", min = 0, max = 0 ) @CommandPermissions("worldedit.region.naturalize") @Logging(REGION) public void naturalize(Player player, EditSession editSession, @Selection Region region) throws WorldEditException { int affected = editSession.naturalizeCuboidBlocks(region); player.print(affected + " block(s) have been made to look more natural."); } @Command( aliases = { "/walls" }, usage = "", desc = "Build the four sides of the selection", min = 1, max = 1 ) @CommandPermissions("worldedit.region.walls") @Logging(REGION) public void walls(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { int affected = editSession.makeWalls(region, pattern); player.print(affected + " block(s) have been changed."); } @Command( aliases = { "/faces", "/outline" }, usage = "", desc = "Build the walls, ceiling, and floor of a selection", min = 1, max = 1 ) @CommandPermissions("worldedit.region.faces") @Logging(REGION) public void faces(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { int affected = editSession.makeCuboidFaces(region, pattern); player.print(affected + " block(s) have been changed."); } @Command( aliases = { "/smooth" }, usage = "[iterations] [filter]", desc = "Smooth the elevation in the selection", help = "Smooths the elevation in the selection.\n" + "Optionally, restricts the height map to a set of blocks specified with mask syntax.\n" + "For example, '//smooth 1 grass_block,dirt,stone' would only smooth natural surface terrain.", min = 0, max = 2 ) @CommandPermissions("worldedit.region.smooth") @Logging(REGION) public void smooth(Player player, EditSession editSession, @Selection Region region, @Optional("1") int iterations, @Optional Mask mask) throws WorldEditException { HeightMap heightMap = new HeightMap(editSession, region, mask); HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0)); int affected = heightMap.applyFilter(filter, iterations); player.print("Terrain's height map smoothed. " + affected + " block(s) changed."); } @Command( aliases = { "/move" }, usage = "[count] [direction] [leave-id]", flags = "sa", desc = "Move the contents of the selection", help = "Moves the contents of the selection.\n" + "The -s flag shifts the selection to the target location.\n" + "The -a flag skips air blocks.\n" + "Optionally fills the old location with .", min = 0, max = 3 ) @CommandPermissions("worldedit.region.move") @Logging(ORIENTATION_REGION) public void move(Player player, EditSession editSession, LocalSession session, @Selection Region region, @Optional("1") @Range(min = 1) int count, @Optional(Direction.AIM) @Direction(includeDiagonals = true) BlockVector3 direction, @Optional("air") Pattern replace, @Switch('s') boolean moveSelection, @Switch('a') boolean ignoreAirBlocks) throws WorldEditException { int affected = editSession.moveRegion(region, direction, count, !ignoreAirBlocks, replace); if (moveSelection) { try { region.shift(direction.multiply(count)); session.getRegionSelector(player.getWorld()).learnChanges(); session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); } catch (RegionOperationException e) { player.printError(e.getMessage()); } } player.print(affected + " blocks moved."); } @Command( aliases = { "/stack" }, usage = "[count] [direction]", flags = "sa", desc = "Repeat the contents of the selection", help = "Repeats the contents of the selection.\n" + "Flags:\n" + " -s shifts the selection to the last stacked copy\n" + " -a skips air blocks", min = 0, max = 2 ) @CommandPermissions("worldedit.region.stack") @Logging(ORIENTATION_REGION) public void stack(Player player, EditSession editSession, LocalSession session, @Selection Region region, @Optional("1") @Range(min = 1) int count, @Optional(Direction.AIM) @Direction(includeDiagonals = true) BlockVector3 direction, @Switch('s') boolean moveSelection, @Switch('a') boolean ignoreAirBlocks) throws WorldEditException { int affected = editSession.stackCuboidRegion(region, direction, count, !ignoreAirBlocks); if (moveSelection) { try { final BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()); final BlockVector3 shiftVector = direction.toVector3().multiply(count * (Math.abs(direction.dot(size)) + 1)).toBlockPoint(); region.shift(shiftVector); session.getRegionSelector(player.getWorld()).learnChanges(); session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); } catch (RegionOperationException e) { player.printError(e.getMessage()); } } player.print(affected + " blocks changed. Undo with //undo"); } @Command( aliases = { "/regen" }, usage = "", desc = "Regenerates the contents of the selection", help = "Regenerates the contents of the current selection.\n" + "This command might affect things outside the selection,\n" + "if they are within the same chunk.", min = 0, max = 0 ) @CommandPermissions("worldedit.regen") @Logging(REGION) public void regenerateChunk(Player player, LocalSession session, EditSession editSession, @Selection Region region) throws WorldEditException { Mask mask = session.getMask(); try { session.setMask((Mask) null); player.getWorld().regenerate(region, editSession); } finally { session.setMask(mask); } player.print("Region regenerated."); } @Command( aliases = { "/deform" }, usage = "", desc = "Deforms a selected region with an expression", help = "Deforms a selected region with an expression\n" + "The expression is executed for each block and is expected\n" + "to modify the variables x, y and z to point to a new block\n" + "to fetch. See also tinyurl.com/wesyntax.", flags = "ro", min = 1, max = -1 ) @CommandPermissions("worldedit.region.deform") @Logging(ALL) public void deform(Player player, LocalSession session, EditSession editSession, @Selection Region region, @Text String expression, @Switch('r') boolean useRawCoords, @Switch('o') boolean offset) throws WorldEditException { final Vector3 zero; Vector3 unit; if (useRawCoords) { zero = Vector3.ZERO; unit = Vector3.ONE; } else if (offset) { zero = session.getPlacementPosition(player).toVector3(); unit = Vector3.ONE; } else { final Vector3 min = region.getMinimumPoint().toVector3(); final Vector3 max = region.getMaximumPoint().toVector3(); zero = max.add(min).divide(2); unit = max.subtract(zero); if (unit.getX() == 0) unit = unit.withX(1.0); if (unit.getY() == 0) unit = unit.withY(1.0); if (unit.getZ() == 0) unit = unit.withZ(1.0); } try { final int affected = editSession.deformRegion(region, zero, unit, expression, session.getTimeout()); player.findFreePosition(); player.print(affected + " block(s) have been deformed."); } catch (ExpressionException e) { player.printError(e.getMessage()); } } @Command( aliases = { "/hollow" }, usage = "[[ ]]", desc = "Hollows out the object contained in this selection", help = "Hollows out the object contained in this selection.\n" + "Optionally fills the hollowed out part with the given block.\n" + "Thickness is measured in manhattan distance.", min = 0, max = 2 ) @CommandPermissions("worldedit.region.hollow") @Logging(REGION) public void hollow(Player player, EditSession editSession, @Selection Region region, @Optional("0") @Range(min = 0) int thickness, @Optional("air") Pattern pattern) throws WorldEditException { int affected = editSession.hollowOutRegion(region, thickness, pattern); player.print(affected + " block(s) have been changed."); } @Command( aliases = { "/forest" }, usage = "[type] [density]", desc = "Make a forest within the region", min = 0, max = 2 ) @CommandPermissions("worldedit.region.forest") @Logging(REGION) public void forest(Player player, EditSession editSession, @Selection Region region, @Optional("tree") TreeType type, @Optional("5") @Range(min = 0, max = 100) double density) throws WorldEditException { int affected = editSession.makeForest(region, density / 100, type); player.print(affected + " trees created."); } @Command( aliases = { "/flora" }, usage = "[density]", desc = "Make flora within the region", min = 0, max = 1 ) @CommandPermissions("worldedit.region.flora") @Logging(REGION) public void flora(Player player, EditSession editSession, @Selection Region region, @Optional("10") @Range(min = 0, max = 100) double density) throws WorldEditException { density = density / 100; FloraGenerator generator = new FloraGenerator(editSession); GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator); LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground); visitor.setMask(new NoiseFilter2D(new RandomNoise(), density)); Operations.completeLegacy(visitor); player.print(ground.getAffected() + " flora created."); } }