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 85fbde830..95bffb681 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -127,6 +127,7 @@ import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.NullRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionIntersection; +import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.regions.Regions; import com.sk89q.worldedit.regions.shape.ArbitraryBiomeShape; import com.sk89q.worldedit.regions.shape.ArbitraryShape; @@ -1804,18 +1805,59 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public int stackCuboidRegion( - Region region, BlockVector3 offset, int count, - boolean copyEntities, boolean copyBiomes, Mask mask + Region region, + BlockVector3 offset, + int count, + boolean copyEntities, + boolean copyBiomes, + Mask mask ) throws MaxChangedBlocksException { checkNotNull(region); checkNotNull(offset); + + BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1); + try { + return stackRegionBlockUnits(region, offset.multiply(size), count, copyEntities, copyBiomes, mask); + } catch (RegionOperationException e) { + // Should never be able to happen + throw new AssertionError(e); + } + } + + /** + * Stack a region using block units. + * + * @param region the region to stack + * @param offset how far to move the contents each stack in block units + * @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 + * @throws RegionOperationException thrown if the region operation is invalid + */ + public int stackRegionBlockUnits( + Region region, + BlockVector3 offset, + int count, + boolean copyEntities, + boolean copyBiomes, + Mask mask + ) throws MaxChangedBlocksException, RegionOperationException { + checkNotNull(region); + checkNotNull(offset); checkArgument(count >= 1, "count >= 1 required"); BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1); + BlockVector3 offsetAbs = offset.abs(); + if (offsetAbs.x() < size.x() && offsetAbs.y() < size.y() && offsetAbs.z() < size.z()) { + throw new RegionOperationException(Caption.of("worldedit.stack.intersecting-region")); + } BlockVector3 to = region.getMinimumPoint(); ForwardExtentCopy copy = new ForwardExtentCopy(this, region, this, to); copy.setRepetitions(count); - copy.setTransform(new AffineTransform().translate(offset.multiply(size))); + copy.setTransform(new AffineTransform().translate(offset)); copy.setCopyingEntities(copyEntities); copy.setCopyingBiomes(copyBiomes); final Region allowedRegion; 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 565aecb9a..2847542a5 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 @@ -594,7 +594,7 @@ public class RegionCommands { session.getRegionSelector(world).learnChanges(); session.getRegionSelector(world).explainRegionAdjust(actor, session); } catch (RegionOperationException e) { - actor.printError(TextComponent.of(e.getMessage())); + actor.printError(e.getRichMessage()); } } @@ -640,7 +640,7 @@ public class RegionCommands { int count, @Arg(desc = "How far to move the contents each stack", def = Offset.FORWARD) @Offset - BlockVector3 direction, + BlockVector3 offset, @Switch(name = 's', desc = "Shift the selection to the last stacked copy") boolean moveSelection, @Switch(name = 'a', desc = "Ignore air blocks") @@ -649,6 +649,8 @@ public class RegionCommands { boolean copyEntities, @Switch(name = 'b', desc = "Also copy biomes") boolean copyBiomes, + @Switch(name = 'r', desc = "Use block units") + boolean blockUnits, @ArgFlag(name = 'm', desc = "Set the include mask, non-matching blocks become air") Mask mask ) throws WorldEditException { @@ -668,19 +670,24 @@ public class RegionCommands { combinedMask = mask; } - int affected = editSession.stackCuboidRegion(region, direction, count, copyEntities, copyBiomes, combinedMask); + int affected; + if (blockUnits) { + affected = editSession.stackRegionBlockUnits(region, offset, count, copyEntities, copyBiomes, combinedMask); + } else { + affected = editSession.stackCuboidRegion(region, offset, count, copyEntities, copyBiomes, combinedMask); + } if (moveSelection) { try { final BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1); - - final BlockVector3 shiftVector = direction.multiply(size).multiply(count); + final BlockVector3 shiftSize = blockUnits ? offset : offset.multiply(size); + final BlockVector3 shiftVector = shiftSize.multiply(count); region.shift(shiftVector); session.getRegionSelector(world).learnChanges(); session.getRegionSelector(world).explainRegionAdjust(actor, session); } catch (RegionOperationException e) { - actor.printError(TextComponent.of(e.getMessage())); + actor.printError(e.getRichMessage()); } } diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index fea3e7dd5..0b7a63b8d 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -478,6 +478,7 @@ "worldedit.curve.convex-only": "//curve only works with convex polyhedral selections", "worldedit.replace.replaced": "{0} blocks have been replaced.", "worldedit.stack.changed": "{0} blocks changed. Undo with //undo", + "worldedit.stack.intersecting-region": "Stack offset must not collide with the region when using block units", "worldedit.regen.regenerated": "Region regenerated.", "worldedit.regen.failed": "Unable to regenerate chunks. Check console for details.", "worldedit.walls.changed": "{0} blocks have been changed.",