From ddacb976e4bc0e1140a3ce6061f5fd95c5f8e3f5 Mon Sep 17 00:00:00 2001 From: Jordan Date: Tue, 23 Jul 2024 19:53:12 +0200 Subject: [PATCH] fix: improve SchemGen, allow null mask (reordered command args) (#2817) - fixes #2815 --- .../command/tool/brush/PopulateSchem.java | 3 +- .../core/function/generator/SchemGen.java | 53 +++++++++++++++---- .../java/com/sk89q/worldedit/EditSession.java | 2 +- .../worldedit/command/BrushCommands.java | 4 +- .../com/sk89q/worldedit/extent/Extent.java | 25 ++++++--- 5 files changed, 67 insertions(+), 20 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/PopulateSchem.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/PopulateSchem.java index b28d7536b..59fc2add2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/PopulateSchem.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/PopulateSchem.java @@ -24,7 +24,8 @@ public record PopulateSchem(Mask mask, List clipboards, int rar CuboidRegion cuboid = new CuboidRegion( editSession.getWorld(), position.subtract(size1, size1, size1), - position.add(size1, size1, size1) + position.add(size1, size1, size1), + true ); try { editSession.addSchems(cuboid, mask, clipboards, rarity, randomRotate); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/SchemGen.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/SchemGen.java index 250914c05..83207f50b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/SchemGen.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/SchemGen.java @@ -7,6 +7,7 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.ClipboardHolder; import java.util.List; @@ -19,29 +20,60 @@ public class SchemGen implements Resource { private final List clipboards; private final boolean randomRotate; private final Mask mask; + private final Region region; private final MutableBlockVector3 mutable = new MutableBlockVector3(); + /** + * @deprecated Use {@link SchemGen#SchemGen(Mask, Extent, List, boolean, Region)} + */ + @Deprecated(forRemoval = true, since = "TODO") public SchemGen(Mask mask, Extent extent, List clipboards, boolean randomRotate) { this.mask = mask; this.extent = extent; this.clipboards = clipboards; this.randomRotate = randomRotate; + this.region = null; + } + + /** + * New instance. Places a schematic on terrain at a given x,z when appropriate + * + * @since TODO + */ + public SchemGen(Mask mask, Extent extent, List clipboards, boolean randomRotate, Region region) { + this.mask = mask; + this.extent = extent; + this.clipboards = clipboards; + this.randomRotate = randomRotate; + this.region = region; + } + + private int getY(int x, int z) { + if (region == null) { + return extent.getNearestSurfaceTerrainBlock( + x, + z, + mutable.y(), + this.extent.getMinY(), + this.extent.getMaxY(), + Integer.MIN_VALUE, + Integer.MAX_VALUE + ); + } else { + int y = extent.getHighestTerrainBlock(x, z, region.getMinimumY(), region.getMaximumY(), mask); + if (y == region.getMinimumY() && !extent.getBlock(x, y, z).getMaterial().isMovementBlocker()) { + return Integer.MIN_VALUE; + } + return y; + } } @Override public boolean spawn(Random random, int x, int z) throws WorldEditException { mutable.mutX(x); mutable.mutZ(z); - int y = extent.getNearestSurfaceTerrainBlock( - x, - z, - mutable.y(), - this.extent.getMinY(), - this.extent.getMaxY(), - Integer.MIN_VALUE, - Integer.MAX_VALUE - ); + int y = getY(x, z); if (y == Integer.MIN_VALUE || y == Integer.MAX_VALUE) { return false; } @@ -54,7 +86,8 @@ public class SchemGen implements Resource { if (randomRotate) { holder.setTransform(new AffineTransform().rotateY(ThreadLocalRandom.current().nextInt(4) * 90)); } - Clipboard clipboard = holder.getClipboard(); + Clipboard clipboard = holder.getClipboards().size() == 1 ? holder.getClipboard() : + holder.getClipboards().get(ThreadLocalRandom.current().nextInt(clipboards.size())); Transform transform = holder.getTransform(); if (transform.isIdentity()) { clipboard.paste(extent, mutable, false); 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 1469ffb96..926265bfb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -3881,7 +3881,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { @Override public void addSchems(Region region, Mask mask, List clipboards, int rarity, boolean rotate) throws WorldEditException { - spawnResource(region, new SchemGen(mask, this, clipboards, rotate), rarity, 1); + spawnResource(region, new SchemGen(mask, this, clipboards, rotate, region), rarity, 1); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 7160be750..664e656c4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -645,10 +645,10 @@ public class BrushCommands { @CommandPermissions("worldedit.brush.populateschematic") public void scatterSchemBrush( Player player, InjectedValueAccess context, - @Arg(desc = "Mask") - Mask mask, @Arg(name = "clipboard", desc = "Clipboard uri") String clipboardStr, + @Arg(desc = "Mask of block to place on. Defaults to solid blocks.", def = "") + Mask mask, @Arg(desc = "Expression", def = "30") Expression radius, @Arg(desc = "double", def = "50") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java index 6117c6850..1b4420d27 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -216,7 +216,7 @@ public interface Extent extends InputExtent, OutputExtent { */ /** - * Returns the highest solid 'terrain' block. + * Returns the highest solid 'terrain' (movement-blocking) block. * * @param x the X coordinate * @param z the Z coordinate @@ -225,6 +225,9 @@ public interface Extent extends InputExtent, OutputExtent { * @return height of highest block found or 'minY' */ default int getHighestTerrainBlock(final int x, final int z, int minY, int maxY) { + maxY = Math.min(maxY, getMaxY()); + minY = Math.max(getMinY(), minY); + for (int y = maxY; y >= minY; --y) { BlockState block = getBlock(x, y, z); if (block.getBlockType().getMaterial().isMovementBlocker()) { @@ -235,7 +238,7 @@ public interface Extent extends InputExtent, OutputExtent { } /** - * Returns the highest solid 'terrain' block. + * Returns the highest block matching the given mask. * * @param x the X coordinate * @param z the Z coordinate @@ -245,6 +248,9 @@ public interface Extent extends InputExtent, OutputExtent { * @return height of highest block found or 'minY' */ default int getHighestTerrainBlock(final int x, final int z, int minY, int maxY, Mask filter) { + if (filter == null) { + return getHighestTerrainBlock(x, z, minY, maxY); + } maxY = Math.min(maxY, getMaxY()); minY = Math.max(getMinY(), minY); @@ -259,9 +265,7 @@ public interface Extent extends InputExtent, OutputExtent { } /** - * Returns the nearest surface layer (up/down from start) - *

- * TODO: Someone understand this..? + * Returns the nearest surface layer (up/down from start), where a layer is 1/16th of a block to allow for snow, liquid, etc. * * @param x x to search from * @param z y to search from @@ -271,6 +275,9 @@ public interface Extent extends InputExtent, OutputExtent { * @return nearest surface layer */ default int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { + maxY = Math.min(maxY, getMaxY()); + minY = Math.max(getMinY(), minY); + int clearanceAbove = maxY - y; int clearanceBelow = y - minY; int clearance = Math.min(clearanceAbove, clearanceBelow); @@ -331,6 +338,9 @@ public interface Extent extends InputExtent, OutputExtent { * @return The y value of the nearest terrain block */ default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) { + maxY = Math.min(maxY, getMaxY()); + minY = Math.max(getMinY(), minY); + y = Math.max(minY, Math.min(maxY, y)); int clearanceAbove = maxY - y; int clearanceBelow = y - minY; @@ -438,6 +448,9 @@ public interface Extent extends InputExtent, OutputExtent { int failedMax, boolean ignoreAir ) { + maxY = Math.min(maxY, getMaxY()); + minY = Math.max(getMinY(), minY); + y = Math.max(minY, Math.min(maxY, y)); int clearanceAbove = maxY - y; int clearanceBelow = y - minY; @@ -494,7 +507,7 @@ public interface Extent extends InputExtent, OutputExtent { default void addSchems(Region region, Mask mask, List clipboards, int rarity, boolean rotate) throws WorldEditException { - spawnResource(region, new SchemGen(mask, this, clipboards, rotate), rarity, 1); + spawnResource(region, new SchemGen(mask, this, clipboards, rotate, region), rarity, 1); } default void spawnResource(Region region, Resource gen, int rarity, int frequency) throws WorldEditException {