Superpickaxe + Masks

Add radius mask
Add adjacent mask
Fix superpickaxe error
Fix BFS
This commit is contained in:
Jesse Boyd 2016-09-25 03:37:29 +10:00
parent d5b7605f5e
commit cbade2cec0
10 changed files with 284 additions and 23 deletions

View File

@ -35,7 +35,9 @@ import com.sk89q.worldedit.command.ScriptingCommands;
import com.sk89q.worldedit.command.ToolCommands; import com.sk89q.worldedit.command.ToolCommands;
import com.sk89q.worldedit.command.ToolUtilCommands; import com.sk89q.worldedit.command.ToolUtilCommands;
import com.sk89q.worldedit.command.composition.SelectionCommand; 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.LongRangeBuildTool;
import com.sk89q.worldedit.command.tool.RecursivePickaxe;
import com.sk89q.worldedit.command.tool.brush.GravityBrush; import com.sk89q.worldedit.command.tool.brush.GravityBrush;
import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.event.extent.EditSessionEvent;
import com.sk89q.worldedit.extension.factory.DefaultMaskParser; import com.sk89q.worldedit.extension.factory.DefaultMaskParser;
@ -351,6 +353,8 @@ public class Fawe {
// Brushes // Brushes
GravityBrush.inject(); // Fix for instant placement assumption GravityBrush.inject(); // Fix for instant placement assumption
LongRangeBuildTool.inject(); LongRangeBuildTool.inject();
AreaPickaxe.inject(); // Fixes
RecursivePickaxe.inject(); // Fixes
// Selectors // Selectors
CuboidRegionSelector.inject(); // Translations CuboidRegionSelector.inject(); // Translations
// Visitors // Visitors

View File

@ -34,22 +34,7 @@ public class RecurseBrush implements Brush {
} }
final BlockReplace replace = new BlockReplace(editSession, to); final BlockReplace replace = new BlockReplace(editSession, to);
editSession.setMask((Mask) null); editSession.setMask((Mask) null);
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace) { RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, radius);
@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;
}
}
};
visitor.visit(position); visitor.visit(position);
Operations.completeBlindly(visitor); Operations.completeBlindly(visitor);
} }

View File

@ -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;
}
}

View File

@ -5,6 +5,13 @@ import com.sk89q.worldedit.function.mask.Mask;
import java.util.List; import java.util.List;
public abstract class CustomMask implements Mask { 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) { public CustomMask(List<Mask> masks, String component, ParserContext context) {
try { try {
this.getClass(). getConstructor ( List.class, String.class, ParserContext.class ) ; this.getClass(). getConstructor ( List.class, String.class, ParserContext.class ) ;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -1,10 +1,12 @@
package com.sk89q.worldedit.extension.factory; 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.AngleMask;
import com.boydti.fawe.object.mask.CustomMask; import com.boydti.fawe.object.mask.CustomMask;
import com.boydti.fawe.object.mask.DataMask; import com.boydti.fawe.object.mask.DataMask;
import com.boydti.fawe.object.mask.IdDataMask; import com.boydti.fawe.object.mask.IdDataMask;
import com.boydti.fawe.object.mask.IdMask; 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.XAxisMask;
import com.boydti.fawe.object.mask.YAxisMask; import com.boydti.fawe.object.mask.YAxisMask;
import com.boydti.fawe.object.mask.ZAxisMask; import com.boydti.fawe.object.mask.ZAxisMask;
@ -54,7 +56,7 @@ public class DefaultMaskParser extends InputParser<Mask> {
super(worldEdit); super(worldEdit);
} }
private static CustomMask[] customMasks; private static CustomMask[] customMasks = new CustomMask[0];
public void addMask(CustomMask mask) { public void addMask(CustomMask mask) {
checkNotNull(mask); checkNotNull(mask);
@ -135,16 +137,34 @@ public class DefaultMaskParser extends InputParser<Mask> {
case '/': { case '/': {
String[] split = component.substring(1).split(","); String[] split = component.substring(1).split(",");
if (split.length != 2) { if (split.length != 2) {
throw new InputParseException("Unknown angle '" + component + "' (not in form /#,#)"); throw new InputParseException("Unknown angle '" + component + "' (not in form `/#,#`)");
} }
try { try {
int y1 = Integer.parseInt(split[0]); int y1 = Integer.parseInt(split[0]);
int y2 = Integer.parseInt(split[1]); int y2 = Integer.parseInt(split[1]);
return new AngleMask(extent, y1, y2); return new AngleMask(extent, y1, y2);
} catch (NumberFormatException e) { } 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 '>':
case '<': case '<':
Mask submask; Mask submask;

View File

@ -21,9 +21,14 @@ public abstract class BreadthFirstSearch implements Operation {
private final List<Vector> directions = new ArrayList<>(); private final List<Vector> directions = new ArrayList<>();
private final Map<Node, Integer> visited; private final Map<Node, Integer> visited;
private final ArrayDeque<Node> queue; private final ArrayDeque<Node> queue;
private final int maxDepth;
private int affected = 0; private int affected = 0;
public BreadthFirstSearch(final RegionFunction function) { public BreadthFirstSearch(final RegionFunction function) {
this(function, Integer.MAX_VALUE);
}
public BreadthFirstSearch(final RegionFunction function, int maxDepth) {
this.queue = new ArrayDeque<>(); this.queue = new ArrayDeque<>();
this.visited = new LinkedHashMap<>(); this.visited = new LinkedHashMap<>();
this.function = function; 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(1, 0, 0));
this.directions.add(new Vector(0, 0, -1)); this.directions.add(new Vector(0, 0, -1));
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); public abstract boolean isVisitable(Vector from, Vector to);
@ -53,6 +59,7 @@ public abstract class BreadthFirstSearch implements Operation {
public void visit(final Vector pos) { public void visit(final Vector pos) {
Node node = new Node((int) pos.x, (int) pos.y, (int) pos.z); Node node = new Node((int) pos.x, (int) pos.y, (int) pos.z);
if (!this.visited.containsKey(node)) { if (!this.visited.containsKey(node)) {
isVisitable(pos, pos); // Ignore this, just to initialize mask on this point
visited.put(node, 0); visited.put(node, 0);
queue.add(node); queue.add(node);
} }
@ -67,8 +74,19 @@ public abstract class BreadthFirstSearch implements Operation {
Vector mutable2 = new Vector(); Vector mutable2 = new Vector();
boolean shouldTrim = false; boolean shouldTrim = false;
IntegerTrio[] dirs = getIntDirections(); IntegerTrio[] dirs = getIntDirections();
for (int layer = 0; !queue.isEmpty(); layer++) { for (int layer = 0; !queue.isEmpty() && layer <= maxDepth; layer++) {
int size = queue.size(); 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++) { for (int i = 0; i < size; i++) {
from = queue.poll(); from = queue.poll();
mutable.x = from.getX(); mutable.x = from.getX();

View File

@ -22,7 +22,6 @@ package com.sk89q.worldedit.function.visitor;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operations;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -35,14 +34,18 @@ public class RecursiveVisitor extends BreadthFirstSearch {
private final Mask mask; private final Mask mask;
public RecursiveVisitor(final Mask mask, final RegionFunction function) {
this(mask, function, Integer.MAX_VALUE);
}
/** /**
* Create a new recursive visitor. * Create a new recursive visitor.
* *
* @param mask the mask * @param mask the mask
* @param function the function * @param function the function
*/ */
public RecursiveVisitor(final Mask mask, final RegionFunction function) { public RecursiveVisitor(final Mask mask, final RegionFunction function, int maxDepth) {
super(function); super(function, maxDepth);
checkNotNull(mask); checkNotNull(mask);
this.mask = mask; this.mask = mask;
} }