Superpickaxe + Masks
Add radius mask Add adjacent mask Fix superpickaxe error Fix BFS
This commit is contained in:
parent
d5b7605f5e
commit
cbade2cec0
@ -35,7 +35,9 @@ import com.sk89q.worldedit.command.ScriptingCommands;
|
||||
import com.sk89q.worldedit.command.ToolCommands;
|
||||
import com.sk89q.worldedit.command.ToolUtilCommands;
|
||||
import com.sk89q.worldedit.command.composition.SelectionCommand;
|
||||
import com.sk89q.worldedit.command.tool.AreaPickaxe;
|
||||
import com.sk89q.worldedit.command.tool.LongRangeBuildTool;
|
||||
import com.sk89q.worldedit.command.tool.RecursivePickaxe;
|
||||
import com.sk89q.worldedit.command.tool.brush.GravityBrush;
|
||||
import com.sk89q.worldedit.event.extent.EditSessionEvent;
|
||||
import com.sk89q.worldedit.extension.factory.DefaultMaskParser;
|
||||
@ -351,6 +353,8 @@ public class Fawe {
|
||||
// Brushes
|
||||
GravityBrush.inject(); // Fix for instant placement assumption
|
||||
LongRangeBuildTool.inject();
|
||||
AreaPickaxe.inject(); // Fixes
|
||||
RecursivePickaxe.inject(); // Fixes
|
||||
// Selectors
|
||||
CuboidRegionSelector.inject(); // Translations
|
||||
// Visitors
|
||||
|
@ -34,22 +34,7 @@ public class RecurseBrush implements Brush {
|
||||
}
|
||||
final BlockReplace replace = new BlockReplace(editSession, to);
|
||||
editSession.setMask((Mask) null);
|
||||
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace) {
|
||||
@Override
|
||||
public boolean isVisitable(Vector from, Vector to) {
|
||||
if (super.isVisitable(from, to)) {
|
||||
int dx = Math.abs((int) (position.x - to.x));
|
||||
if (dx > radius) return false;
|
||||
int dz = Math.abs((int) (position.z - to.z));
|
||||
if (dz > radius) return false;
|
||||
int dy = Math.abs((int) (position.y - to.y));
|
||||
if (dy > radius) return false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, radius);
|
||||
visitor.visit(position);
|
||||
Operations.completeBlindly(visitor);
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
package com.boydti.fawe.object.mask;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.function.mask.BlockMask;
|
||||
import java.util.Collection;
|
||||
|
||||
public class AdjacentMask extends BlockMask {
|
||||
public AdjacentMask(Extent extent, Collection<BaseBlock> blocks) {
|
||||
super(extent, blocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Vector v) {
|
||||
double x = v.x;
|
||||
double y = v.x;
|
||||
double z = v.x;
|
||||
v.x = x + 1;
|
||||
if (super.test(v)) { v.x = x; return true; }
|
||||
v.x = x - 1;
|
||||
if (super.test(v)) { v.x = x; return true; }
|
||||
v.x = x;
|
||||
v.y = y + 1;
|
||||
if (super.test(v)) { v.y = y; return true; }
|
||||
v.y = y - 1;
|
||||
if (super.test(v)) { v.y = y; return true; }
|
||||
v.y = y;
|
||||
v.z = z + 1;
|
||||
if (super.test(v)) { v.z = z; return true; }
|
||||
v.z = z - 1;
|
||||
if (super.test(v)) { v.z = z; return true; }
|
||||
v.z = z;
|
||||
return false;
|
||||
}
|
||||
}
|
@ -5,6 +5,13 @@ import com.sk89q.worldedit.function.mask.Mask;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class CustomMask implements Mask {
|
||||
|
||||
/**
|
||||
* Constructor for custom mask
|
||||
* @param masks Any previous masks set (usually from //mask [previous] [thismask]
|
||||
* @param component The input to parse
|
||||
* @param context The context (for extent, player etc)
|
||||
*/
|
||||
public CustomMask(List<Mask> masks, String component, ParserContext context) {
|
||||
try {
|
||||
this.getClass(). getConstructor ( List.class, String.class, ParserContext.class ) ;
|
||||
|
@ -0,0 +1,52 @@
|
||||
package com.boydti.fawe.object.mask;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.Mask2D;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class RadiusMask implements Mask, ResettableMask{
|
||||
|
||||
private final int minSqr, maxSqr;
|
||||
|
||||
public RadiusMask(int min, int max) {
|
||||
this.minSqr = min * min;
|
||||
this.maxSqr = max * max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
pos = null;
|
||||
}
|
||||
|
||||
private Vector pos;
|
||||
|
||||
@Override
|
||||
public boolean test(Vector to) {
|
||||
if (pos == null) {
|
||||
pos = new Vector(to);
|
||||
}
|
||||
int dx = Math.abs((int) (pos.x - to.x));
|
||||
int dy = Math.abs((int) (pos.x - to.x));
|
||||
int dz = Math.abs((int) (pos.x - to.x));
|
||||
int d = dx * dx;
|
||||
if (d < minSqr || d > maxSqr) {
|
||||
return false;
|
||||
}
|
||||
d += dz * dz;
|
||||
if (d < minSqr || d > maxSqr) {
|
||||
return false;
|
||||
}
|
||||
d += dy * dy;
|
||||
if (d < minSqr || d > maxSqr) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Mask2D toMask2D() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.sk89q.worldedit.command.tool;
|
||||
|
||||
import com.sk89q.worldedit.*;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.blocks.BlockID;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
/**
|
||||
* A super pickaxe mode that will remove blocks in an area.
|
||||
*/
|
||||
public class AreaPickaxe implements BlockTool {
|
||||
|
||||
private static final BaseBlock air = new BaseBlock(0);
|
||||
private int range;
|
||||
|
||||
public AreaPickaxe(int range) {
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(Actor player) {
|
||||
return player.hasPermission("worldedit.superpickaxe.area");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) {
|
||||
int ox = clicked.getBlockX();
|
||||
int oy = clicked.getBlockY();
|
||||
int oz = clicked.getBlockZ();
|
||||
int initialType = ((World) clicked.getExtent()).getBlockType(clicked.toVector());
|
||||
|
||||
if (initialType == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (initialType == BlockID.BEDROCK && !player.canDestroyBedrock()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
EditSession editSession = session.createEditSession(player);
|
||||
editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop);
|
||||
|
||||
for (int x = ox - range; x <= ox + range; ++x) {
|
||||
for (int y = oy - range; y <= oy + range; ++y) {
|
||||
for (int z = oz - range; z <= oz + range; ++z) {
|
||||
if (editSession.getLazyBlock(x, y, z).getId() != initialType) {
|
||||
continue;
|
||||
}
|
||||
editSession.setBlock(x, y, z, air);
|
||||
}
|
||||
}
|
||||
}
|
||||
editSession.flushQueue();
|
||||
session.remember(editSession);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Class<?> inject() {
|
||||
return AreaPickaxe.class;
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package com.sk89q.worldedit.command.tool;
|
||||
|
||||
import com.boydti.fawe.object.mask.IdMask;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.blocks.BlockID;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.function.block.BlockReplace;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.operation.Operations;
|
||||
import com.sk89q.worldedit.function.pattern.BlockPattern;
|
||||
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
/**
|
||||
* A pickaxe mode that recursively finds adjacent blocks within range of
|
||||
* an initial block and of the same type.
|
||||
*/
|
||||
public class RecursivePickaxe implements BlockTool {
|
||||
|
||||
private static final BaseBlock air = new BaseBlock(0);
|
||||
private double range;
|
||||
|
||||
public RecursivePickaxe(double range) {
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(Actor player) {
|
||||
return player.hasPermission("worldedit.superpickaxe.recursive");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) {
|
||||
World world = (World) clicked.getExtent();
|
||||
final Vector pos = clicked.toVector();
|
||||
|
||||
EditSession editSession = session.createEditSession(player);
|
||||
|
||||
BaseBlock block = editSession.getBlock(pos);
|
||||
int initialType = block.getType();
|
||||
|
||||
if (initialType == BlockID.AIR || (initialType == BlockID.BEDROCK && !player.canDestroyBedrock())) {
|
||||
editSession.flushQueue();
|
||||
return true;
|
||||
}
|
||||
|
||||
editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop);
|
||||
|
||||
final int radius = (int) range;
|
||||
final BlockReplace replace = new BlockReplace(editSession, new BlockPattern(editSession.nullBlock));
|
||||
editSession.setMask((Mask) null);
|
||||
RecursiveVisitor visitor = new RecursiveVisitor(new IdMask(editSession), replace, radius);
|
||||
visitor.visit(pos);
|
||||
Operations.completeBlindly(visitor);
|
||||
|
||||
editSession.flushQueue();
|
||||
session.remember(editSession);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Class<?> inject() {
|
||||
return RecursivePickaxe.class;
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
package com.sk89q.worldedit.extension.factory;
|
||||
|
||||
import com.boydti.fawe.object.mask.AdjacentMask;
|
||||
import com.boydti.fawe.object.mask.AngleMask;
|
||||
import com.boydti.fawe.object.mask.CustomMask;
|
||||
import com.boydti.fawe.object.mask.DataMask;
|
||||
import com.boydti.fawe.object.mask.IdDataMask;
|
||||
import com.boydti.fawe.object.mask.IdMask;
|
||||
import com.boydti.fawe.object.mask.RadiusMask;
|
||||
import com.boydti.fawe.object.mask.XAxisMask;
|
||||
import com.boydti.fawe.object.mask.YAxisMask;
|
||||
import com.boydti.fawe.object.mask.ZAxisMask;
|
||||
@ -54,7 +56,7 @@ public class DefaultMaskParser extends InputParser<Mask> {
|
||||
super(worldEdit);
|
||||
}
|
||||
|
||||
private static CustomMask[] customMasks;
|
||||
private static CustomMask[] customMasks = new CustomMask[0];
|
||||
|
||||
public void addMask(CustomMask mask) {
|
||||
checkNotNull(mask);
|
||||
@ -135,16 +137,34 @@ public class DefaultMaskParser extends InputParser<Mask> {
|
||||
case '/': {
|
||||
String[] split = component.substring(1).split(",");
|
||||
if (split.length != 2) {
|
||||
throw new InputParseException("Unknown angle '" + component + "' (not in form /#,#)");
|
||||
throw new InputParseException("Unknown angle '" + component + "' (not in form `/#,#`)");
|
||||
}
|
||||
try {
|
||||
int y1 = Integer.parseInt(split[0]);
|
||||
int y2 = Integer.parseInt(split[1]);
|
||||
return new AngleMask(extent, y1, y2);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InputParseException("Unknown angle '" + component + "' (not in form /#,#)");
|
||||
throw new InputParseException("Unknown angle '" + component + "' (not in form `/#,#`)");
|
||||
}
|
||||
}
|
||||
case '{':
|
||||
String[] split = component.substring(1).split(",");
|
||||
if (split.length != 2) {
|
||||
throw new InputParseException("Unknown range '" + component + "' (not in form `{#,#`)");
|
||||
}
|
||||
try {
|
||||
int y1 = Integer.parseInt(split[0]);
|
||||
int y2 = Integer.parseInt(split[1]);
|
||||
return new RadiusMask(y1, y2);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InputParseException("Unknown range '" + component + "' (not in form `{#,#`)");
|
||||
}
|
||||
case '~': {
|
||||
ParserContext tempContext = new ParserContext(context);
|
||||
tempContext.setRestricted(false);
|
||||
tempContext.setPreferringWildcard(true);
|
||||
return new AdjacentMask(extent, worldEdit.getBlockFactory().parseFromListInput(component.substring(1), tempContext));
|
||||
}
|
||||
case '>':
|
||||
case '<':
|
||||
Mask submask;
|
||||
|
@ -21,9 +21,14 @@ public abstract class BreadthFirstSearch implements Operation {
|
||||
private final List<Vector> directions = new ArrayList<>();
|
||||
private final Map<Node, Integer> visited;
|
||||
private final ArrayDeque<Node> queue;
|
||||
private final int maxDepth;
|
||||
private int affected = 0;
|
||||
|
||||
public BreadthFirstSearch(final RegionFunction function) {
|
||||
this(function, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public BreadthFirstSearch(final RegionFunction function, int maxDepth) {
|
||||
this.queue = new ArrayDeque<>();
|
||||
this.visited = new LinkedHashMap<>();
|
||||
this.function = function;
|
||||
@ -33,6 +38,7 @@ public abstract class BreadthFirstSearch implements Operation {
|
||||
this.directions.add(new Vector(1, 0, 0));
|
||||
this.directions.add(new Vector(0, 0, -1));
|
||||
this.directions.add(new Vector(0, 0, 1));
|
||||
this.maxDepth = maxDepth;
|
||||
}
|
||||
|
||||
public abstract boolean isVisitable(Vector from, Vector to);
|
||||
@ -53,6 +59,7 @@ public abstract class BreadthFirstSearch implements Operation {
|
||||
public void visit(final Vector pos) {
|
||||
Node node = new Node((int) pos.x, (int) pos.y, (int) pos.z);
|
||||
if (!this.visited.containsKey(node)) {
|
||||
isVisitable(pos, pos); // Ignore this, just to initialize mask on this point
|
||||
visited.put(node, 0);
|
||||
queue.add(node);
|
||||
}
|
||||
@ -67,8 +74,19 @@ public abstract class BreadthFirstSearch implements Operation {
|
||||
Vector mutable2 = new Vector();
|
||||
boolean shouldTrim = false;
|
||||
IntegerTrio[] dirs = getIntDirections();
|
||||
for (int layer = 0; !queue.isEmpty(); layer++) {
|
||||
for (int layer = 0; !queue.isEmpty() && layer <= maxDepth; layer++) {
|
||||
int size = queue.size();
|
||||
if (layer == maxDepth) {
|
||||
visited.clear();
|
||||
for (Node current : queue) {
|
||||
mutable.x = current.getX();
|
||||
mutable.y = current.getY();
|
||||
mutable.z = current.getZ();
|
||||
function.apply(mutable);
|
||||
affected++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
from = queue.poll();
|
||||
mutable.x = from.getX();
|
||||
|
@ -22,7 +22,6 @@ package com.sk89q.worldedit.function.visitor;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.function.RegionFunction;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.operation.Operations;
|
||||
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -35,14 +34,18 @@ public class RecursiveVisitor extends BreadthFirstSearch {
|
||||
|
||||
private final Mask mask;
|
||||
|
||||
public RecursiveVisitor(final Mask mask, final RegionFunction function) {
|
||||
this(mask, function, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new recursive visitor.
|
||||
*
|
||||
* @param mask the mask
|
||||
* @param function the function
|
||||
*/
|
||||
public RecursiveVisitor(final Mask mask, final RegionFunction function) {
|
||||
super(function);
|
||||
public RecursiveVisitor(final Mask mask, final RegionFunction function, int maxDepth) {
|
||||
super(function, maxDepth);
|
||||
checkNotNull(mask);
|
||||
this.mask = mask;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user