diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 183f7542f..aff049e9a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -40,6 +40,7 @@ import com.sk89q.worldedit.extent.world.FastModeExtent; import com.sk89q.worldedit.extent.world.SurvivalModeExtent; import com.sk89q.worldedit.function.GroundFunction; import com.sk89q.worldedit.function.RegionMaskingFilter; +import com.sk89q.worldedit.function.biome.BiomeReplace; import com.sk89q.worldedit.function.block.BlockDistributionCounter; import com.sk89q.worldedit.function.block.BlockReplace; import com.sk89q.worldedit.function.block.Counter; @@ -66,6 +67,7 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.WaterloggedRemover; import com.sk89q.worldedit.function.util.RegionOffset; import com.sk89q.worldedit.function.visitor.DownwardVisitor; +import com.sk89q.worldedit.function.visitor.FlatRegionVisitor; import com.sk89q.worldedit.function.visitor.LayerVisitor; import com.sk89q.worldedit.function.visitor.NonRisingVisitor; import com.sk89q.worldedit.function.visitor.RecursiveVisitor; @@ -1206,7 +1208,8 @@ public class EditSession implements Extent, AutoCloseable { } /** - * Stack a cuboid region. + * Stack a cuboid region. For compatibility, entities are copied by biomes are not. + * Use {@link #stackCuboidRegion(Region, BlockVector3, int, boolean, boolean, Mask)} to fine tune. * * @param region the region to stack * @param dir the direction to stack @@ -1216,6 +1219,23 @@ public class EditSession implements Extent, AutoCloseable { * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public int stackCuboidRegion(Region region, BlockVector3 dir, int count, boolean copyAir) throws MaxChangedBlocksException { + return stackCuboidRegion(region, dir, count, true, false, copyAir ? null : new ExistingBlockMask(this)); + } + + /** + * Stack a cuboid region. + * + * @param region the region to stack + * @param dir the direction to stack + * @param count the number of times to stack + * @param copyEntities true to copy entities + * @param copyBiomes true to copy biomes + * @param mask source mask for the operation (only matching blocks are copied) + * @return number of blocks affected + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + public int stackCuboidRegion(Region region, BlockVector3 dir, int count, + boolean copyEntities, boolean copyBiomes, Mask mask) throws MaxChangedBlocksException { checkNotNull(region); checkNotNull(dir); checkArgument(count >= 1, "count >= 1 required"); @@ -1225,8 +1245,10 @@ public class EditSession implements Extent, AutoCloseable { ForwardExtentCopy copy = new ForwardExtentCopy(this, region, this, to); copy.setRepetitions(count); copy.setTransform(new AffineTransform().translate(dir.multiply(size))); - if (!copyAir) { - copy.setSourceMask(new ExistingBlockMask(this)); + copy.setCopyingEntities(copyEntities); + copy.setCopyingBiomes(copyBiomes); + if (mask != null) { + copy.setSourceMask(mask); } Operations.completeLegacy(copy); return copy.getAffected(); @@ -1244,9 +1266,29 @@ public class EditSession implements Extent, AutoCloseable { * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public int moveRegion(Region region, BlockVector3 dir, int distance, boolean copyAir, Pattern replacement) throws MaxChangedBlocksException { + return moveRegion(region, dir, distance, true, false, copyAir ? new ExistingBlockMask(this) : null, replacement); + } + + /** + * Move the blocks in a region a certain direction. + * + * @param region the region to move + * @param dir the direction + * @param distance the distance to move + * @param moveEntities true to move entities + * @param copyBiomes true to copy biomes (source biome is unchanged) + * @param mask source mask for the operation (only matching blocks are moved) + * @param replacement the replacement pattern to fill in after moving, or null to use air + * @return number of blocks moved + * @throws MaxChangedBlocksException thrown if too many blocks are changed + * @throws IllegalArgumentException thrown if the region is not a flat region, but copyBiomes is true + */ + public int moveRegion(Region region, BlockVector3 dir, int distance, + boolean moveEntities, boolean copyBiomes, Mask mask, Pattern replacement) throws MaxChangedBlocksException { checkNotNull(region); checkNotNull(dir); checkArgument(distance >= 1, "distance >= 1 required"); + checkArgument(!copyBiomes || region instanceof FlatRegion, "can't copy biomes from non-flat region"); BlockVector3 to = region.getMinimumPoint(); @@ -1261,9 +1303,13 @@ public class EditSession implements Extent, AutoCloseable { ForwardExtentCopy copy = new ForwardExtentCopy(this, region, buffer, to); copy.setTransform(new AffineTransform().translate(dir.multiply(distance))); copy.setSourceFunction(remove); // Remove - copy.setRemovingEntities(true); - if (!copyAir) { - copy.setSourceMask(new ExistingBlockMask(this)); + + copy.setCopyingEntities(moveEntities); + copy.setRemovingEntities(moveEntities); + copy.setCopyingBiomes(copyBiomes); + + if (mask != null) { + copy.setSourceMask(mask); } // Then we need to copy the buffer to the world @@ -1271,6 +1317,13 @@ public class EditSession implements Extent, AutoCloseable { RegionVisitor visitor = new RegionVisitor(buffer.asRegion(), replace); OperationQueue operation = new OperationQueue(copy, visitor); + + if (copyBiomes) { + BiomeReplace biomeReplace = new BiomeReplace(this, buffer); + FlatRegionVisitor biomeVisitor = new FlatRegionVisitor((FlatRegion) buffer.asRegion(), biomeReplace); + operation.offer(biomeVisitor); + } + Operations.completeLegacy(operation); return copy.getAffected(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 0871f8b8b..e4241dc49 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -34,6 +34,7 @@ import com.sk89q.worldedit.function.block.BlockReplace; 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.MaskIntersection; import com.sk89q.worldedit.function.mask.NoiseFilter2D; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; @@ -56,6 +57,7 @@ import com.sk89q.worldedit.util.TreeGenerator.TreeType; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; +import org.enginehub.piston.annotation.param.ArgFlag; import org.enginehub.piston.annotation.param.Switch; import java.util.ArrayList; @@ -293,10 +295,27 @@ public class RegionCommands { @Switch(name = 's', desc = "Shift the selection to the target location") boolean moveSelection, @Switch(name = 'a', desc = "Ignore air blocks") - boolean ignoreAirBlocks) throws WorldEditException { + boolean ignoreAirBlocks, + @Switch(name = 'e', desc = "Also copy entities") + boolean copyEntities, + @Switch(name = 'b', desc = "Also copy biomes") + boolean copyBiomes, + @ArgFlag(name = 'm', desc = "Set the include mask, non-matching blocks become air", def = "") + Mask mask) throws WorldEditException { checkCommandArgument(count >= 1, "Count must be >= 1"); - int affected = editSession.moveRegion(region, direction, count, !ignoreAirBlocks, replace); + Mask combinedMask; + if (ignoreAirBlocks) { + if (mask == null) { + combinedMask = new ExistingBlockMask(editSession); + } else { + combinedMask = new MaskIntersection(mask, new ExistingBlockMask(editSession)); + } + } else { + combinedMask = mask; + } + + int affected = editSession.moveRegion(region, direction, count, copyEntities, copyBiomes, combinedMask, replace); if (moveSelection) { try { @@ -329,8 +348,26 @@ public class RegionCommands { @Switch(name = 's', desc = "Shift the selection to the last stacked copy") boolean moveSelection, @Switch(name = 'a', desc = "Ignore air blocks") - boolean ignoreAirBlocks) throws WorldEditException { - int affected = editSession.stackCuboidRegion(region, direction, count, !ignoreAirBlocks); + boolean ignoreAirBlocks, + @Switch(name = 'e', desc = "Also copy entities") + boolean copyEntities, + @Switch(name = 'b', desc = "Also copy biomes") + boolean copyBiomes, + @ArgFlag(name = 'm', desc = "Set the include mask, non-matching blocks become air", def = "") + Mask mask) throws WorldEditException { + + Mask combinedMask; + if (ignoreAirBlocks) { + if (mask == null) { + combinedMask = new ExistingBlockMask(editSession); + } else { + combinedMask = new MaskIntersection(mask, new ExistingBlockMask(editSession)); + } + } else { + combinedMask = mask; + } + + int affected = editSession.stackCuboidRegion(region, direction, count, copyEntities, copyBiomes, combinedMask); if (moveSelection) { try { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java index 0aa410916..299ea5a40 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java @@ -25,12 +25,17 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.Mask2D; import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.pattern.BiomePattern; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.AbstractRegion; +import com.sk89q.worldedit.regions.AbstractFlatRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionOperationException; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; @@ -46,12 +51,16 @@ import java.util.Map; *
This buffer will not attempt to return results from the buffer when * accessor methods (such as {@link #getBlock(BlockVector3)}) are called.
*/ -public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pattern { +public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pattern, BiomePattern { private final Map