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 846bc0794..e6051f754 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -57,6 +57,7 @@ import com.sk89q.worldedit.function.GroundFunction; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.block.BlockReplace; import com.sk89q.worldedit.function.block.Naturalizer; +import com.sk89q.worldedit.function.block.SnowSimulator; import com.sk89q.worldedit.function.generator.ForestGenerator; import com.sk89q.worldedit.function.generator.GardenPatchGenerator; import com.sk89q.worldedit.function.mask.BlockStateMask; @@ -97,6 +98,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.MathUtils; import com.sk89q.worldedit.math.MutableBlockVector2; import com.sk89q.worldedit.math.MutableBlockVector3; +import com.sk89q.worldedit.math.Vector2; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.interpolation.Interpolation; import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation; @@ -104,6 +106,7 @@ import com.sk89q.worldedit.math.interpolation.Node; import com.sk89q.worldedit.math.noise.RandomNoise; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.CylinderRegion; import com.sk89q.worldedit.regions.EllipsoidRegion; import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; @@ -148,6 +151,7 @@ import java.util.UUID; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.function.block.SnowSimulator.snowy; import static com.sk89q.worldedit.regions.Regions.asFlatRegion; import static com.sk89q.worldedit.regions.Regions.maximumBlockY; import static com.sk89q.worldedit.regions.Regions.minimumBlockY; @@ -2150,9 +2154,25 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @param radius the radius * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed + * @deprecated Use {@link #thaw(BlockVector3, double, int)}. */ @Deprecated public int thaw(BlockVector3 position, double radius) + throws MaxChangedBlocksException { + return thaw(position, radius, + WorldEdit.getInstance().getConfiguration().defaultVerticalHeight); + } + + /** + * Thaw blocks in a cylinder. + * + * @param position the position + * @param radius the radius + * @param height the height (upwards and downwards) + * @return number of blocks affected + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + public int thaw(BlockVector3 position, double radius, int height) throws MaxChangedBlocksException { int affected = 0; double radiusSq = radius * radius; @@ -2164,6 +2184,10 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { BlockState air = BlockTypes.AIR.getDefaultState(); BlockState water = BlockTypes.WATER.getDefaultState(); + int centerY = Math.max(getWorld().getMinY(), Math.min(getWorld().getMaxY(), oy)); + int minY = Math.max(getWorld().getMinY(), centerY - height); + int maxY = Math.min(getWorld().getMaxY(), centerY + height); + int ceilRadius = (int) Math.ceil(radius); for (int x = ox - ceilRadius; x <= ox + ceilRadius; ++x) { for (int z = oz - ceilRadius; z <= oz + ceilRadius; ++z) { @@ -2171,15 +2195,25 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { continue; } - for (int y = maxY; y >= 1; --y) { - BlockType id = getBlock(x, y, z).getBlockType(); + for (int y = maxY; y > minY; --y) { + BlockVector3 pt = BlockVector3.at(x, y, z); + BlockVector3 below = BlockVector3.at(x, y - 1, z); + BlockType id = getBlock(pt).getBlockType(); if (id == BlockTypes.ICE) { - if (setBlock(x, y, z, water)) { + if (setBlock(pt, water)) { ++affected; } } else if (id == BlockTypes.SNOW) { - if (setBlock(x, y, z, air)) { + if (setBlock(pt, air)) { + if (y > 0 ) { + BlockState block = getBlock(below); + if (block.getStates().containsKey(snowy)) { + if (setBlock(below, block.with(snowy, false))) { + affected++; + } + } + } ++affected; } } else if (id.getMaterial().isAir()) { @@ -2191,7 +2225,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { } } - return changes = affected; + return affected; } /** @@ -2201,67 +2235,46 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @param radius a radius * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed + * @deprecated Use {@link #simulateSnow(BlockVector3, double, int)}. */ @Deprecated public int simulateSnow(BlockVector3 position, double radius) throws MaxChangedBlocksException { - int affected = 0; - double radiusSq = radius * radius; - - int ox = position.getBlockX(); - int oy = position.getBlockY(); - int oz = position.getBlockZ(); - - BlockState ice = BlockTypes.ICE.getDefaultState(); - BlockState snow = BlockTypes.SNOW.getDefaultState(); - - int ceilRadius = (int) Math.ceil(radius); - for (int x = ox - ceilRadius; x <= ox + ceilRadius; ++x) { - for (int z = oz - ceilRadius; z <= oz + ceilRadius; ++z) { - if ((BlockVector3.at(x, oy, z)).distanceSq(position) > radiusSq) { - continue; - } - - for (int y = maxY; y >= 1; --y) { - BlockVector3 pt = BlockVector3.at(x, y, z); - BlockType id = getBlock(pt).getBlockType(); - - if (id.getMaterial().isAir()) { - continue; - } - - // Ice! - if (id == BlockTypes.WATER) { - if (setBlock(pt, ice)) { - ++affected; - } - break; - } - - // Snow should not cover these blocks - if (id.getMaterial().isTranslucent()) { - // Add snow on leaves - if (!BlockCategories.LEAVES.contains(id)) { - break; - } - } - - // Too high? - if (y == maxY) { - break; - } - - // add snow cover - if (setBlock(pt.add(0, 1, 0), snow)) { - ++affected; - } - break; - } - } - } - - return changes = affected; + return simulateSnow(position, radius, + WorldEdit.getInstance().getConfiguration().defaultVerticalHeight); } + /** + * Make snow in a cylinder. + * + * @param position a position + * @param radius a radius + * @param height the height (upwards and downwards) + * @return number of blocks affected + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + public int simulateSnow(BlockVector3 position, double radius, int height) + throws MaxChangedBlocksException { + + return simulateSnow(new CylinderRegion(position, Vector2.at(radius, radius), position.getBlockY(), height), false); + } + + /** + * Make snow in a region. + * + * @param region the region to simulate snow in + * @param stack whether it should stack existing snow + * @return number of blocks affected + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + public int simulateSnow(FlatRegion region, boolean stack) + throws MaxChangedBlocksException { + checkNotNull(region); + + SnowSimulator snowSimulator = new SnowSimulator(this, stack); + LayerVisitor layerVisitor = new LayerVisitor(region, region.getMinimumY(), region.getMaximumY(), snowSimulator); + Operations.completeLegacy(layerVisitor); + return snowSimulator.getAffected(); + } /** * Make dirt green. * @@ -2270,52 +2283,67 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @param onlyNormalDirt only affect normal dirt (all default properties) * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed + * @deprecated Use {@link #green(BlockVector3, double, int, boolean)}. */ @Deprecated public int green(BlockVector3 position, double radius, boolean onlyNormalDirt) throws MaxChangedBlocksException { + return green(position, radius, + WorldEdit.getInstance().getConfiguration().defaultVerticalHeight, onlyNormalDirt); + } + + /** + * Make dirt green in a cylinder. + * + * @param position the position + * @param radius the radius + * @param height the height + * @param onlyNormalDirt only affect normal dirt (all default properties) + * @return number of blocks affected + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + public int green(BlockVector3 position, double radius, int height, boolean onlyNormalDirt) + throws MaxChangedBlocksException { + int affected = 0; final double radiusSq = radius * radius; final int ox = position.getBlockX(); + final int oy = position.getBlockY(); final int oz = position.getBlockZ(); final BlockState grass = BlockTypes.GRASS_BLOCK.getDefaultState(); + final int centerY = Math.max(getWorld().getMinY(), Math.min(getWorld().getMaxY(), oy)); + final int minY = Math.max(getWorld().getMinY(), centerY - height); + final int maxY = Math.min(getWorld().getMaxY(), centerY + height); + final int ceilRadius = (int) Math.ceil(radius); for (int x = ox - ceilRadius; x <= ox + ceilRadius; ++x) { - int dx = x - ox; - int dx2 = dx * dx; for (int z = oz - ceilRadius; z <= oz + ceilRadius; ++z) { - int dz = z - oz; - int dz2 = dz * dz; - if (dx2 + dz2 > radiusSq) { + if ((BlockVector3.at(x, oy, z)).distanceSq(position) > radiusSq) { continue; } - loop: - for (int y = maxY; y >= 1; --y) { - final BlockType block = getBlockType(x, y, z); - switch (block.getInternalId()) { - case BlockID.COARSE_DIRT: - if (onlyNormalDirt) { - break loop; - } - this.setBlock(x, y, z, grass); - break loop; - case BlockID.DIRT: - this.setBlock(x, y, z, grass); - break loop; - case BlockID.WATER: - case BlockID.LAVA: - default: - if (block.getMaterial().isMovementBlocker()) { - break loop; - } + + for (int y = maxY; y > minY; --y) { + final BlockVector3 pt = BlockVector3.at(x, y, z); + final BlockState block = getBlock(pt); + + if (block.getBlockType() == BlockTypes.DIRT + || (!onlyNormalDirt && block.getBlockType() == BlockTypes.COARSE_DIRT)) { + if (setBlock(pt, grass)) { + ++affected; + } + break; + } else if (block.getBlockType() == BlockTypes.WATER || block.getBlockType() == BlockTypes.LAVA) { + break; + } else if (block.getBlockType().getMaterial().isMovementBlocker()) { + break; } } } } - return changes; + return affected; } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index c6d94f35c..c41f244a5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -34,6 +34,7 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.command.argument.HeightConverter; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.CreatureButcher; @@ -54,10 +55,12 @@ import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.EntityVisitor; import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.annotation.VertHeight; import com.sk89q.worldedit.internal.expression.EvaluationException; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector2; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CylinderRegion; import com.sk89q.worldedit.regions.Region; @@ -464,6 +467,7 @@ public class UtilityCommands { return affected; } + @Command( name = "snow", aliases = { "/snow" }, @@ -472,13 +476,27 @@ public class UtilityCommands { @CommandPermissions("worldedit.snow") @Logging(PLACEMENT) public int snow(Actor actor, LocalSession session, EditSession editSession, - @Arg(desc = "The radius of the circle to snow in", def = "10") - double size) throws WorldEditException { + @Arg(desc = "The radius of the cylinder to snow in", def = "10") + double size, + @Arg( + desc = "The height of the cylinder to snow in", + def = HeightConverter.DEFAULT_VALUE + ) + @VertHeight + int height, + @Switch(name = 's', desc = "Stack snow layers") + boolean stack) throws WorldEditException { size = Math.max(1, size); + height = Math.max(1, height); we.checkMaxRadius(size); - int affected = editSession.simulateSnow(session.getPlacementPosition(actor), size); - actor.printInfo(TranslatableComponent.of("worldedit.snow.created", TextComponent.of(affected))); + BlockVector3 position = session.getPlacementPosition(actor); + + CylinderRegion region = new CylinderRegion(position, Vector2.at(size, size), position.getBlockY() - height, position.getBlockY() + height); + int affected = editSession.simulateSnow(region, stack); + actor.printInfo(TranslatableComponent.of( + "worldedit.snow.created", TextComponent.of(affected) + )); return affected; } @@ -490,13 +508,22 @@ public class UtilityCommands { @CommandPermissions("worldedit.thaw") @Logging(PLACEMENT) public int thaw(Actor actor, LocalSession session, EditSession editSession, - @Arg(desc = "The radius of the circle to thaw in", def = "10") - double size) throws WorldEditException { + @Arg(desc = "The radius of the cylinder to thaw in", def = "10") + double size, + @Arg( + desc = "The height of the cylinder to thaw in", + def = HeightConverter.DEFAULT_VALUE + ) + @VertHeight + int height) throws WorldEditException { size = Math.max(1, size); + height = Math.max(1, height); we.checkMaxRadius(size); - int affected = editSession.thaw(session.getPlacementPosition(actor), size); - actor.printInfo(TranslatableComponent.of("worldedit.thaw.removed", TextComponent.of(affected))); + int affected = editSession.thaw(session.getPlacementPosition(actor), size, height); + actor.printInfo(TranslatableComponent.of( + "worldedit.thaw.removed", TextComponent.of(affected) + )); return affected; } @@ -508,16 +535,27 @@ public class UtilityCommands { @CommandPermissions("worldedit.green") @Logging(PLACEMENT) public int green(Actor actor, LocalSession session, EditSession editSession, - @Arg(desc = "The radius of the circle to convert in", def = "10") + @Arg(desc = "The radius of the cylinder to convert in", def = "10") double size, + @Arg( + desc = "The height of the cylinder to convert in", + def = HeightConverter.DEFAULT_VALUE + ) + @VertHeight + int height, @Switch(name = 'f', desc = "Also convert coarse dirt") boolean convertCoarse) throws WorldEditException { size = Math.max(1, size); + height = Math.max(1, height); we.checkMaxRadius(size); final boolean onlyNormalDirt = !convertCoarse; - final int affected = editSession.green(session.getPlacementPosition(actor), size, onlyNormalDirt); - actor.printInfo(TranslatableComponent.of("worldedit.green.changed", TextComponent.of(affected))); + final int affected = editSession.green( + session.getPlacementPosition(actor), size, height, onlyNormalDirt + ); + actor.printInfo(TranslatableComponent.of( + "worldedit.green.changed", TextComponent.of(affected) + )); return affected; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/SnowSimulator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/SnowSimulator.java index c1bc4d0f3..6dc337cb9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/SnowSimulator.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/SnowSimulator.java @@ -23,12 +23,15 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.LayerFunction; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.registry.state.BooleanProperty; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; public class SnowSimulator implements LayerFunction { + public static final BooleanProperty snowy = (BooleanProperty) (Property) BlockTypes.GRASS_BLOCK.getProperty("snowy"); + private final BlockState ice = BlockTypes.ICE.getDefaultState(); private final BlockState snow = BlockTypes.SNOW.getDefaultState(); private final BlockState snowBlock = BlockTypes.SNOW_BLOCK.getDefaultState(); @@ -42,6 +45,7 @@ public class SnowSimulator implements LayerFunction { private int affected; public SnowSimulator(Extent extent, boolean stack) { + this.extent = extent; this.stack = stack; @@ -66,9 +70,8 @@ public class SnowSimulator implements LayerFunction { return true; } - // Can only place on full solid blocks - return block.getBlockType().getMaterial().isFullCube() - && block.getBlockType().getMaterial().isSolid(); + // Stop searching when we hit a movement blocker + return block.getBlockType().getMaterial().isMovementBlocker(); } @Override @@ -107,16 +110,25 @@ public class SnowSimulator implements LayerFunction { // We've hit the highest layer (If it doesn't contain current + 2 it means it's 1 away from full) if (!snowLayersProperty.getValues().contains(currentHeight + 2)) { if (this.extent.setBlock(abovePosition, snowBlock)) { + if (block.getStates().containsKey(snowy)) { + this.extent.setBlock(position, block.with(snowy, true)); + } this.affected++; } } else { if (this.extent.setBlock(abovePosition, above.with(snowLayersProperty, currentHeight + 1))) { + if (block.getStates().containsKey(snowy)) { + this.extent.setBlock(position, block.with(snowy, true)); + } this.affected++; } } return false; } if (this.extent.setBlock(abovePosition, snow)) { + if (block.getStates().containsKey(snowy)) { + this.extent.setBlock(position, block.with(snowy, true)); + } this.affected++; } return false;