From d765b24b2717654374cdf206591f0acb2e2f3683 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Sat, 11 Mar 2017 20:50:37 +1100 Subject: [PATCH] Some new brushes Scatter - Set a pattern at random points on a surface ScatterCommand - Runs a command at random points on a surface Splatter - Recursively set blocks at random points on a surface --- .../com/boydti/fawe/command/FaweParser.java | 26 +----- .../main/java/com/boydti/fawe/config/BBC.java | 1 + .../fawe/object/brush/CommandBrush.java | 13 +-- .../fawe/object/brush/ScatterBrush.java | 91 +++++++++++++++++++ .../fawe/object/brush/ScatterCommand.java | 48 ++++++++++ .../fawe/object/brush/SplatterBrush.java | 63 +++++++++++++ .../fawe/object/brush/StencilBrush.java | 4 +- .../object/collection/BlockVectorSet.java | 25 +++++ .../collection/LocalBlockVectorSet.java | 25 +++++ .../java/com/boydti/fawe/util/StringMan.java | 25 +++++ .../worldedit/command/BrushCommands.java | 64 ++++++++++++- .../extension/factory/DefaultMaskParser.java | 4 +- .../factory/HashTagPatternParser.java | 6 +- .../function}/pattern/BlockPattern.java | 1 - .../function/visitor/BreadthFirstSearch.java | 9 +- 15 files changed, 362 insertions(+), 43 deletions(-) create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/ScatterBrush.java create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/ScatterCommand.java create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/SplatterBrush.java rename core/src/main/java/com/{boydti/fawe/object => sk89q/worldedit/function}/pattern/BlockPattern.java (94%) diff --git a/core/src/main/java/com/boydti/fawe/command/FaweParser.java b/core/src/main/java/com/boydti/fawe/command/FaweParser.java index fc67b4e9..013c83d3 100644 --- a/core/src/main/java/com/boydti/fawe/command/FaweParser.java +++ b/core/src/main/java/com/boydti/fawe/command/FaweParser.java @@ -5,7 +5,6 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.internal.registry.InputParser; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -14,29 +13,6 @@ public abstract class FaweParser extends InputParser { super(worldEdit); } - public List split(String input, char delim) { - List result = new ArrayList(); - int start = 0; - int bracket = 0; - boolean inQuotes = false; - for (int current = 0; current < input.length(); current++) { - char currentChar = input.charAt(current); - boolean atLastChar = (current == input.length() - 1); - if (!atLastChar && (bracket > 0 || (currentChar == '{' && ++bracket > 0) || (current == '}' && --bracket <= 0))) continue; - if (currentChar == '\"') inQuotes = !inQuotes; // toggle state - if(atLastChar) result.add(input.substring(start)); - else if (currentChar == delim && !inQuotes) { - String toAdd = input.substring(start, current); - if (toAdd.startsWith("\"")) { - toAdd = toAdd.substring(1, toAdd.length() - 1); - } - result.add(toAdd); - start = current + 1; - } - } - return result; - } - public T catchSuggestion(String currentInput, String nextInput, ParserContext context) throws InputParseException { try { return parseFromInput(nextInput, context); @@ -47,7 +23,7 @@ public abstract class FaweParser extends InputParser { } public List suggestRemaining(String input, String... expected) throws InputParseException { - List remainder = split(input, ':'); + List remainder = StringMan.split(input, ':'); int len = remainder.size(); if (len != expected.length - 1) { if (len <= expected.length - 1 && len != 0) { diff --git a/core/src/main/java/com/boydti/fawe/config/BBC.java b/core/src/main/java/com/boydti/fawe/config/BBC.java index 68b4a288..00622966 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -121,6 +121,7 @@ public enum BBC { BRUSH_HEIGHT_INVALID("Invalid height map file (%s0)", "WorldEdit.Brush"), BRUSH_SMOOTH("Smooth brush equipped (%s0 x %s1 using %s2. Note: Use the blend brush if you want to smooth overhangs or caves.).", "WorldEdit.Brush"), BRUSH_SPHERE("Sphere brush shape equipped (%s0).", "WorldEdit.Brush"), + BRUSH_SCATTER("Scatter brush shape equipped (%s0, %s1).", "WorldEdit.Brush"), BRUSH_SHATTER("Shatter brush shape equipped (%s0, %s1).", "WorldEdit.Brush"), BRUSH_STENCIL("Stencil brush equipped (%s0).", "WorldEdit.Brush"), BRUSH_LINE("Line brush shape equipped (%s0).", "WorldEdit.Brush"), diff --git a/core/src/main/java/com/boydti/fawe/object/brush/CommandBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/CommandBrush.java index eb38d109..f6a733f6 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/CommandBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/CommandBrush.java @@ -1,6 +1,7 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.util.StringMan; import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper; import com.boydti.fawe.wrappers.PlayerWrapper; import com.boydti.fawe.wrappers.SilentPlayerWrapper; @@ -15,25 +16,25 @@ import com.sk89q.worldedit.extension.platform.CommandManager; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; import com.sk89q.worldedit.util.Location; +import java.util.List; public class CommandBrush implements Brush { private final String command; - private final int radius; public CommandBrush(String command, double radius) { this.command = command; - this.radius = (int) radius; } @Override public void build(EditSession editSession, Vector position, Pattern pattern, double size) throws MaxChangedBlocksException { + int radius = (int) size; CuboidRegionSelector selector = new CuboidRegionSelector(editSession.getWorld(), position.subtract(radius, radius, radius), position.add(radius, radius, radius)); String replaced = command.replace("{x}", position.getBlockX() + "") - .replace("{y}", position.getBlockY() + "") - .replace("{z}", position.getBlockZ() + "") + .replace("{y}", Integer.toString(position.getBlockY())) + .replace("{z}", Integer.toString(position.getBlockZ())) .replace("{world}", editSession.getQueue().getWorldName()) - .replace("{size}", radius + ""); + .replace("{size}", Integer.toString(radius)); FawePlayer fp = editSession.getPlayer(); Player player = fp.getPlayer(); @@ -45,7 +46,7 @@ public class CommandBrush implements Brush { } fp.setSelection(selector); PlayerWrapper wePlayer = new SilentPlayerWrapper(new LocationMaskedPlayerWrapper(player, new Location(player.getExtent(), position))); - String[] cmds = replaced.split(";"); + List cmds = StringMan.split(replaced, ';'); for (String cmd : cmds) { CommandEvent event = new CommandEvent(wePlayer, cmd); CommandManager.getInstance().handleCommandOnCurrentThread(event); diff --git a/core/src/main/java/com/boydti/fawe/object/brush/ScatterBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/ScatterBrush.java new file mode 100644 index 00000000..f15a718d --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/ScatterBrush.java @@ -0,0 +1,91 @@ +package com.boydti.fawe.object.brush; + +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.object.collection.BlockVectorSet; +import com.boydti.fawe.object.collection.LocalBlockVectorSet; +import com.boydti.fawe.object.mask.AdjacentAnyMask; +import com.boydti.fawe.object.mask.RadiusMask; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.command.tool.brush.Brush; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.mask.SolidBlockMask; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.visitor.BreadthFirstSearch; +import com.sk89q.worldedit.function.visitor.RecursiveVisitor; +import java.util.Arrays; + +public class ScatterBrush implements Brush { + + private final int count; + private final int distance; + private Mask mask; + + public ScatterBrush(int count, int distance) { + this.count = count; + this.distance = distance; + } + + public int getDistance() { + return distance; + } + + public int getCount() { + return count; + } + + @Override + public void build(EditSession editSession, Vector position, Pattern pattern, double size) throws MaxChangedBlocksException { + // pick a bunch of random points + // expand randomly from them + this.mask = editSession.getMask(); + if (this.mask == null) { + this.mask = Masks.alwaysTrue(); + } + final AdjacentAnyMask adjacent = new AdjacentAnyMask(editSession, Arrays.asList(new BaseBlock(0))); + final SolidBlockMask solid = new SolidBlockMask(editSession); + final RadiusMask radius = new RadiusMask(0, (int) size); + + final int distance = Math.min((int) size, this.distance); + + RecursiveVisitor visitor = new RecursiveVisitor(vector -> solid.test(vector) && radius.test(vector) && adjacent.test(vector), function -> true); + visitor.visit(position); + visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS)); + Operations.completeBlindly(visitor); + BlockVectorSet visited = visitor.getVisited(); + int length = visited.size(); + + LocalBlockVectorSet placed = new LocalBlockVectorSet(); + int maxFails = 1000; + for (int i = 0; i < count; i++) { + int index = PseudoRandom.random.nextInt(length); + Vector pos = visited.get(index); + if (pos != null && canApply(editSession, pos)) { + int x = pos.getBlockX(); + int y = pos.getBlockY(); + int z = pos.getBlockZ(); + if (placed.containsRadius(x, y, z, distance)) { + if (maxFails-- <= 0) { + break; + } + i--; + continue; + } + placed.add(x, y, z); + apply(editSession, placed, pos, pattern, size); + } + } + } + + public boolean canApply(EditSession editSession, Vector pos) { + return mask.test(pos); + } + + public void apply(EditSession editSession, LocalBlockVectorSet placed, Vector pt, Pattern p, double size) throws MaxChangedBlocksException { + editSession.setBlock(pt, p); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/brush/ScatterCommand.java b/core/src/main/java/com/boydti/fawe/object/brush/ScatterCommand.java new file mode 100644 index 00000000..3e2444b1 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/ScatterCommand.java @@ -0,0 +1,48 @@ +package com.boydti.fawe.object.brush; + +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.collection.LocalBlockVectorSet; +import com.boydti.fawe.util.StringMan; +import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper; +import com.boydti.fawe.wrappers.PlayerWrapper; +import com.boydti.fawe.wrappers.SilentPlayerWrapper; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.event.platform.CommandEvent; +import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; +import com.sk89q.worldedit.util.Location; +import java.util.List; + +public class ScatterCommand extends ScatterBrush{ + private final String command; + + public ScatterCommand(int count, int distance, String command) { + super(count, distance); + this.command = command; + } + + @Override + public void apply(EditSession editSession, LocalBlockVectorSet placed, Vector position, Pattern p, double size) throws MaxChangedBlocksException { + int radius = getDistance(); + CuboidRegionSelector selector = new CuboidRegionSelector(editSession.getWorld(), position.subtract(radius, radius, radius), position.add(radius, radius, radius)); + String replaced = command.replace("{x}", position.getBlockX() + "") + .replace("{y}", Integer.toString(position.getBlockY())) + .replace("{z}", Integer.toString(position.getBlockZ())) + .replace("{world}", editSession.getQueue().getWorldName()) + .replace("{size}", Integer.toString(radius)); + + FawePlayer fp = editSession.getPlayer(); + Player player = fp.getPlayer(); + fp.setSelection(selector); + PlayerWrapper wePlayer = new SilentPlayerWrapper(new LocationMaskedPlayerWrapper(player, new Location(player.getExtent(), position))); + List cmds = StringMan.split(replaced, ';'); + for (String cmd : cmds) { + CommandEvent event = new CommandEvent(wePlayer, cmd); + CommandManager.getInstance().handleCommandOnCurrentThread(event); + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/SplatterBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/SplatterBrush.java new file mode 100644 index 00000000..13cc5282 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/SplatterBrush.java @@ -0,0 +1,63 @@ +package com.boydti.fawe.object.brush; + +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.object.collection.LocalBlockVectorSet; +import com.boydti.fawe.object.mask.AdjacentAnyMask; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.SolidBlockMask; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.pattern.BlockPattern; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.visitor.RecursiveVisitor; +import java.util.Arrays; + +public class SplatterBrush extends ScatterBrush { + private final boolean solid; + private final int recursion; + + public SplatterBrush(int count, int distance, boolean solid) { + super(count, 1); + this.recursion = distance; + this.solid = solid; + } + + @Override + public void apply(final EditSession editSession, final LocalBlockVectorSet placed, final Vector position, Pattern p, double size) throws MaxChangedBlocksException { + final Pattern finalPattern; + if (solid) { + finalPattern = new BlockPattern(p.apply(position)); + } else { + finalPattern = p; + } + final int size2 = (int) (size * size); + final AdjacentAnyMask adjacent = new AdjacentAnyMask(editSession, Arrays.asList(new BaseBlock(0))); + final SolidBlockMask solid = new SolidBlockMask(editSession); + + RecursiveVisitor visitor = new RecursiveVisitor(new Mask() { + @Override + public boolean test(Vector vector) { + double dist = vector.distanceSq(position); + if (!placed.contains(vector) && (PseudoRandom.random.random(5) < 2) && solid.test(vector) && adjacent.test(vector)) { + placed.add(vector); + return true; + } + return false; + } + }, new RegionFunction() { + @Override + public boolean apply(Vector vector) throws WorldEditException { + return editSession.setBlock(vector, finalPattern); + } + }, recursion, editSession); + visitor.setMaxBranch(2); + visitor.setDirections(Arrays.asList(visitor.DIAGONAL_DIRECTIONS)); + visitor.visit(position); + Operations.completeBlindly(visitor); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java index 939db357..85335a4a 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java @@ -3,7 +3,6 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.object.PseudoRandom; import com.boydti.fawe.object.brush.heightmap.HeightMap; import com.boydti.fawe.object.mask.AdjacentAnyMask; -import com.boydti.fawe.object.mask.RadiusMask; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; @@ -45,11 +44,10 @@ public class StencilBrush extends HeightBrush { final AdjacentAnyMask adjacent = new AdjacentAnyMask(editSession, Arrays.asList(new BaseBlock(0))); final SolidBlockMask solid = new SolidBlockMask(editSession); RegionMask region = new RegionMask(new CuboidRegion(position.subtract(size, size, size), position.add(size, size, size))); - final RadiusMask radius = new RadiusMask(0, size); RecursiveVisitor visitor = new RecursiveVisitor(new Mask() { @Override public boolean test(Vector vector) { - if (solid.test(vector) && radius.test(vector)) { + if (solid.test(vector) && region.test(vector)) { int dx = vector.getBlockX() - cx; int dy = vector.getBlockY() - cy; int dz = vector.getBlockZ() - cz; diff --git a/core/src/main/java/com/boydti/fawe/object/collection/BlockVectorSet.java b/core/src/main/java/com/boydti/fawe/object/collection/BlockVectorSet.java index 7e65ab27..b7b352af 100644 --- a/core/src/main/java/com/boydti/fawe/object/collection/BlockVectorSet.java +++ b/core/src/main/java/com/boydti/fawe/object/collection/BlockVectorSet.java @@ -32,6 +32,31 @@ public class BlockVectorSet extends AbstractCollection implements Set> iter = localSets.int2ObjectEntrySet().iterator(); + while (iter.hasNext()) { + Int2ObjectMap.Entry entry = iter.next(); + LocalBlockVectorSet set = entry.getValue(); + int size = set.size(); + int newSize = count + size; + if (newSize > index) { + int localIndex = index - count; + Vector pos = set.getIndex(localIndex); + if (pos != null) { + int pair = entry.getIntKey(); + int cx = MathMan.unpairX(pair); + int cz = MathMan.unpairY(pair); + pos.mutX((cx << 11) + pos.getBlockX()); + pos.mutZ((cz << 11) + pos.getBlockZ()); + return pos; + } + } + count = newSize; + } + return null; + } + @Override public boolean isEmpty() { for (Int2ObjectMap.Entry entry : localSets.int2ObjectEntrySet()) { diff --git a/core/src/main/java/com/boydti/fawe/object/collection/LocalBlockVectorSet.java b/core/src/main/java/com/boydti/fawe/object/collection/LocalBlockVectorSet.java index edce4ad5..186d959e 100644 --- a/core/src/main/java/com/boydti/fawe/object/collection/LocalBlockVectorSet.java +++ b/core/src/main/java/com/boydti/fawe/object/collection/LocalBlockVectorSet.java @@ -61,6 +61,9 @@ public class LocalBlockVectorSet implements Set { } public boolean containsRadius(int x, int y, int z, int radius) { + if (radius <= 0) { + return contains(x, y, z); + } int length = radius * 2; if (size() < length * length * length) { int index = -1; @@ -97,6 +100,28 @@ public class LocalBlockVectorSet implements Set { this.offsetZ = z; } + public Vector getIndex(int getIndex) { + int size = size(); + if (getIndex > size) { + return null; + } + int index = -1; + for (int i = 0; i < getIndex; i++) { + index = set.nextSetBit(index + 1); + } + if (index != -1) { + int b1 = (index & 0xFF); + int b2 = ((byte) (index >> 8)) & 0x7F; + int b3 = ((byte)(index >> 15)) & 0xFF; + int b4 = ((byte) (index >> 23)) & 0xFF; + int x = offsetX + (((b3 + ((MathMan.unpair8x(b2)) << 8)) << 21) >> 21); + int y = b1; + int z = offsetZ + (((b4 + ((MathMan.unpair8y(b2)) << 8)) << 21) >> 21); + return MutableBlockVector.get(x, y, z); + } + return null; + } + @Override public Iterator iterator() { return new Iterator() { diff --git a/core/src/main/java/com/boydti/fawe/util/StringMan.java b/core/src/main/java/com/boydti/fawe/util/StringMan.java index 38b6f558..c87da3f8 100644 --- a/core/src/main/java/com/boydti/fawe/util/StringMan.java +++ b/core/src/main/java/com/boydti/fawe/util/StringMan.java @@ -1,9 +1,11 @@ package com.boydti.fawe.util; import java.lang.reflect.Array; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -30,6 +32,29 @@ public class StringMan { return sb.toString(); } + public static List split(String input, char delim) { + List result = new ArrayList(); + int start = 0; + int bracket = 0; + boolean inQuotes = false; + for (int current = 0; current < input.length(); current++) { + char currentChar = input.charAt(current); + boolean atLastChar = (current == input.length() - 1); + if (!atLastChar && (bracket > 0 || (currentChar == '{' && ++bracket > 0) || (current == '}' && --bracket <= 0))) continue; + if (currentChar == '\"') inQuotes = !inQuotes; // toggle state + if(atLastChar) result.add(input.substring(start)); + else if (currentChar == delim && !inQuotes) { + String toAdd = input.substring(start, current); + if (toAdd.startsWith("\"")) { + toAdd = toAdd.substring(1, toAdd.length() - 1); + } + result.add(toAdd); + start = current + 1; + } + } + return result; + } + public static int intersection(final Set options, final String[] toCheck) { int count = 0; for (final String check : toCheck) { diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 7f20a892..6bdac07b 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -34,7 +34,10 @@ import com.boydti.fawe.object.brush.HeightBrush; import com.boydti.fawe.object.brush.LineBrush; import com.boydti.fawe.object.brush.RaiseBrush; import com.boydti.fawe.object.brush.RecurseBrush; +import com.boydti.fawe.object.brush.ScatterBrush; +import com.boydti.fawe.object.brush.ScatterCommand; import com.boydti.fawe.object.brush.ShatterBrush; +import com.boydti.fawe.object.brush.SplatterBrush; import com.boydti.fawe.object.brush.SplineBrush; import com.boydti.fawe.object.brush.StencilBrush; import com.boydti.fawe.object.brush.TargetMode; @@ -537,7 +540,8 @@ public class BrushCommands { desc = "Use a height map to paint a surface", help = "Chooses the stencil brush.\n" + - "The -w flag will only apply at maximum saturation", + "The -w flag will only apply at maximum saturation\n" + + "The -r flag will apply random rotation", min = 1, max = -1 ) @@ -561,6 +565,62 @@ public class BrushCommands { player.print(BBC.getPrefix() + BBC.BRUSH_STENCIL.f(radius)); } + @Command( + aliases = { "scatter" }, + usage = " [radius=5] [points=5] [distance=1]", + desc = "Scatter blocks on a surface", + help = + "Chooses the scatter brush.", + min = 1, + max = 4 + ) + @CommandPermissions("worldedit.brush.scatter") + public void scatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") double radius, @Optional("5") double points, @Optional("1") double distance) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + BrushTool tool = session.getBrushTool(player); + tool.setFill(fill); + tool.setSize(radius); + tool.setBrush(new ScatterBrush((int) points, (int) distance), "worldedit.brush.scatter", player); + player.print(BBC.getPrefix() + BBC.BRUSH_SCATTER.f(radius, points)); + } + + @Command( + aliases = { "splatter" }, + usage = " [radius=5] [seeds=1] [recursion=5] [solid=true]", + desc = "Splatter blocks on a surface", + help = + "Chooses the Splatter brush.", + min = 1, + max = 4 + ) + @CommandPermissions("worldedit.brush.splatter") + public void splatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") double radius, @Optional("1") double points, @Optional("5") double recursion, @Optional("true") boolean solid) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + BrushTool tool = session.getBrushTool(player); + tool.setFill(fill); + tool.setSize(radius); + tool.setBrush(new SplatterBrush((int) points, (int) recursion, solid), "worldedit.brush.splatter", player); + player.print(BBC.getPrefix() + BBC.BRUSH_SCATTER.f(radius, points)); + } + + @Command( + aliases = { "scmd", "scattercmd", "scattercommand", "scommand" }, + usage = " ", + desc = "Scatter commands on a surface", + help = + "Chooses the scatter command brush.", + min = 1, + max = -1 + ) + @CommandPermissions("worldedit.brush.scattercommand") + public void scatterCommandBrush(Player player, EditSession editSession, LocalSession session, double radius, double points, double distance, CommandContext args) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + BrushTool tool = session.getBrushTool(player); + tool.setSize(radius); + tool.setBrush(new ScatterCommand((int) points, (int) distance, args.getJoinedStrings(3)), "worldedit.brush.scattercommand", player); + player.print(BBC.getPrefix() + BBC.BRUSH_SCATTER.f(radius, points)); + } + @Command( aliases = { "cylinder", "cyl", "c" }, usage = " [radius] [height]", @@ -822,7 +882,7 @@ public class BrushCommands { max = 99 ) @CommandPermissions("worldedit.brush.command") - public void command(Player player, LocalSession session, @Optional("5") double radius, CommandContext args) throws WorldEditException { + public void command(Player player, LocalSession session, double radius, CommandContext args) throws WorldEditException { BrushTool tool = session.getBrushTool(player); String cmd = args.getJoinedStrings(1); tool.setBrush(new CommandBrush(cmd, radius), "worldedit.brush.copy", player); diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java index 7d37edd7..3d1f54c0 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java @@ -90,13 +90,13 @@ public class DefaultMaskParser extends FaweParser { public Mask parseFromInput(String input, ParserContext context) throws InputParseException { List masks = new ArrayList(); - for (String component : split(input, ' ')) { + for (String component : StringMan.split(input, ' ')) { if (component.isEmpty()) { continue; } HashSet blocks = new HashSet(); List masksUnion = new ArrayList(); - for (String elem : split(component, ',')) { + for (String elem : StringMan.split(component, ',')) { ArrayList list = new ArrayList(); list.add(catchSuggestion(input, list, elem, context)); if (list.size() == 1) { diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java index 66239195..636aad5e 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java @@ -197,7 +197,7 @@ public class HashTagPatternParser extends FaweParser { case "#l": case "#linear": { ArrayList patterns = new ArrayList<>(); - for (String token : split(rest, ',')) { + for (String token : StringMan.split(rest, ',')) { patterns.add(catchSuggestion(input, token, context)); } if (patterns.isEmpty()) { @@ -208,7 +208,7 @@ public class HashTagPatternParser extends FaweParser { case "#l3d": case "#linear3d": { ArrayList patterns = new ArrayList<>(); - for (String token : split(rest, ',')) { + for (String token : StringMan.split(rest, ',')) { patterns.add(catchSuggestion(input, token, context)); } if (patterns.isEmpty()) { @@ -250,7 +250,7 @@ public class HashTagPatternParser extends FaweParser { case "": throw new SuggestInputParseException(input, BundledBlockData.getInstance().getBlockNames()); } - List items = split(input, ','); + List items = StringMan.split(input, ','); if (items.size() == 1) { return new BlockPattern(worldEdit.getBlockFactory().parseFromInput(items.get(0), context)); } diff --git a/core/src/main/java/com/boydti/fawe/object/pattern/BlockPattern.java b/core/src/main/java/com/sk89q/worldedit/function/pattern/BlockPattern.java similarity index 94% rename from core/src/main/java/com/boydti/fawe/object/pattern/BlockPattern.java rename to core/src/main/java/com/sk89q/worldedit/function/pattern/BlockPattern.java index a2e9ffe5..32b46452 100644 --- a/core/src/main/java/com/boydti/fawe/object/pattern/BlockPattern.java +++ b/core/src/main/java/com/sk89q/worldedit/function/pattern/BlockPattern.java @@ -2,7 +2,6 @@ package com.sk89q.worldedit.function.pattern; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.function.pattern.AbstractPattern; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java b/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java index bf051ed0..5852b1a8 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java +++ b/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java @@ -60,6 +60,7 @@ public abstract class BreadthFirstSearch implements Operation { private BlockVectorSet queue; private final int maxDepth; private int affected = 0; + private int maxBranch = Integer.MAX_VALUE; public BreadthFirstSearch(final RegionFunction function) { this(function, Integer.MAX_VALUE); @@ -118,6 +119,10 @@ public abstract class BreadthFirstSearch implements Operation { return visited.contains(pos); } + public void setMaxBranch(int maxBranch) { + this.maxBranch = maxBranch; + } + @Override public Operation resume(RunContext run) throws WorldEditException { MutableBlockVector mutable = new MutableBlockVector(); @@ -151,7 +156,8 @@ public abstract class BreadthFirstSearch implements Operation { } for (Vector from : queue) { if (function.apply(from)) affected++; - for (IntegerTrio direction : dirs) { + for (int i = 0, j = 0; i < dirs.length && j < maxBranch; i++) { + IntegerTrio direction = dirs[i]; int y = from.getBlockY() + direction.y; if (y < 0 || y >= 256) { continue; @@ -163,6 +169,7 @@ public abstract class BreadthFirstSearch implements Operation { mutable2.mutY(y); mutable2.mutZ(z); if (isVisitable(from, mutable2)) { + j++; visited.add(x, y, z); tempQueue.add(x, y, z); }