Add /tracemask. (#474)

Allows setting a mask used for block traces. This allows brush tools to
pass through various materials, such as water (e.g. `/tracemask #solid`
or `/tracemask !air,water`) before starting to build.
By default, a null mask is equivalent to #existing (original behavior).

https://gfycat.com/ImmaculateFrayedCockatiel
This commit is contained in:
wizjany 2019-05-23 21:12:31 -04:00 committed by GitHub
parent d0ef56326a
commit 7b47d9a945
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 150 additions and 37 deletions

View File

@ -21,7 +21,6 @@
package com.sk89q.worldedit.bukkit; package com.sk89q.worldedit.bukkit;
import com.sk89q.util.StringUtil;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
@ -33,7 +32,6 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerCommandSendEvent; import org.bukkit.event.player.PlayerCommandSendEvent;
import org.bukkit.event.player.PlayerGameModeChangeEvent; import org.bukkit.event.player.PlayerGameModeChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
@ -55,12 +53,6 @@ public class WorldEditListener implements Listener {
private WorldEditPlugin plugin; private WorldEditPlugin plugin;
/**
* Called when a player plays an animation, such as an arm swing
*
* @param event Relevant event details
*/
/** /**
* Construct the object; * Construct the object;
* *
@ -112,12 +104,8 @@ public class WorldEditListener implements Listener {
return; return;
} }
try { if (event.getHand() == EquipmentSlot.OFF_HAND) {
if (event.getHand() == EquipmentSlot.OFF_HAND) { return;
return; // TODO api needs to be able to get either hand depending on event
// for now just ignore all off hand interacts
}
} catch (NoSuchMethodError | NoSuchFieldError ignored) {
} }
final Player player = plugin.wrapPlayer(event.getPlayer()); final Player player = plugin.wrapPlayer(event.getPlayer());
@ -143,7 +131,6 @@ public class WorldEditListener implements Listener {
event.setCancelled(true); event.setCancelled(true);
} }
} else if (action == Action.RIGHT_CLICK_BLOCK) { } else if (action == Action.RIGHT_CLICK_BLOCK) {
final Block clickedBlock = event.getClickedBlock(); final Block clickedBlock = event.getClickedBlock();
final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ()); final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());

View File

@ -51,7 +51,7 @@ public class ToolUtilCommands {
@CommandPermissions("worldedit.superpickaxe") @CommandPermissions("worldedit.superpickaxe")
public void togglePickaxe(Player player, LocalSession session, public void togglePickaxe(Player player, LocalSession session,
@Arg(desc = "The new super pickaxe state", def = "") @Arg(desc = "The new super pickaxe state", def = "")
Boolean superPickaxe) throws WorldEditException { Boolean superPickaxe) {
boolean hasSuperPickAxe = session.hasSuperPickAxe(); boolean hasSuperPickAxe = session.hasSuperPickAxe();
if (superPickaxe != null && superPickaxe == hasSuperPickAxe) { if (superPickaxe != null && superPickaxe == hasSuperPickAxe) {
player.printError("Super pickaxe already " + (superPickaxe ? "enabled" : "disabled") + "."); player.printError("Super pickaxe already " + (superPickaxe ? "enabled" : "disabled") + ".");
@ -75,11 +75,10 @@ public class ToolUtilCommands {
public void mask(Player player, LocalSession session, public void mask(Player player, LocalSession session,
@Arg(desc = "The mask to set", def = "") @Arg(desc = "The mask to set", def = "")
Mask mask) throws WorldEditException { Mask mask) throws WorldEditException {
session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setMask(mask);
if (mask == null) { if (mask == null) {
session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setMask(null);
player.print("Brush mask disabled."); player.print("Brush mask disabled.");
} else { } else {
session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setMask(mask);
player.print("Brush mask set."); player.print("Brush mask set.");
} }
} }
@ -122,4 +121,20 @@ public class ToolUtilCommands {
session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setSize(size); session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setSize(size);
player.print("Brush size set."); player.print("Brush size set.");
} }
@Command(
name = "tracemask",
desc = "Set the mask used to stop tool traces"
)
@CommandPermissions("worldedit.brush.options.tracemask")
public void traceMask(Player player, LocalSession session,
@Arg(desc = "The trace mask to set", def = "")
Mask mask) throws WorldEditException {
session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setTraceMask(mask);
if (mask == null) {
player.print("Trace mask disabled.");
} else {
player.print("Trace mask set.");
}
}
} }

View File

@ -34,7 +34,6 @@ import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.MaskIntersection; import com.sk89q.worldedit.function.mask.MaskIntersection;
import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -47,6 +46,7 @@ public class BrushTool implements TraceTool {
protected static int MAX_RANGE = 500; protected static int MAX_RANGE = 500;
protected int range = -1; protected int range = -1;
private Mask mask = null; private Mask mask = null;
private Mask traceMask = null;
private Brush brush = new SphereBrush(); private Brush brush = new SphereBrush();
@Nullable @Nullable
private Pattern material; private Pattern material;
@ -86,6 +86,24 @@ public class BrushTool implements TraceTool {
this.mask = filter; this.mask = filter;
} }
/**
* Get the mask used for identifying where to stop traces.
*
* @return the mask used to stop block traces
*/
public @Nullable Mask getTraceMask() {
return mask;
}
/**
* Set the block mask used for identifying where to stop traces.
*
* @param traceMask the mask used to stop block traces
*/
public void setTraceMask(@Nullable Mask traceMask) {
this.traceMask = traceMask;
}
/** /**
* Set the brush. * Set the brush.
* *
@ -162,8 +180,7 @@ public class BrushTool implements TraceTool {
@Override @Override
public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) { public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) {
Location target = null; Location target = player.getBlockTrace(getRange(), true, traceMask);
target = player.getBlockTrace(getRange(), true);
if (target == null) { if (target == null) {
player.printError("No block in sight!"); player.printError("No block in sight!");

View File

@ -25,6 +25,7 @@ import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits; import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.RegionSelector;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
@ -58,7 +59,6 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool {
} }
return false; return false;
} }
@Override @Override
@ -78,12 +78,13 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool {
return false; return false;
} }
public Location getTarget(Player player) { private Location getTarget(Player player) {
Location target; Location target;
Mask mask = getTraceMask();
if (this.range > -1) { if (this.range > -1) {
target = player.getBlockTrace(getRange(), true); target = player.getBlockTrace(getRange(), true, mask);
} else { } else {
target = player.getBlockTrace(MAX_RANGE); target = player.getBlockTrace(MAX_RANGE, false, mask);
} }
if (target == null) { if (target == null) {

View File

@ -26,6 +26,7 @@ import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
@ -91,8 +92,14 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo
return false; return false;
} }
public Location getTargetFace(Player player) { private Location getTargetFace(Player player) {
Location target = player.getBlockTraceFace(getRange(), true); Location target;
Mask mask = getTraceMask();
if (this.range > -1) {
target = player.getBlockTrace(getRange(), true, mask);
} else {
target = player.getBlockTrace(MAX_RANGE, false, mask);
}
if (target == null) { if (target == null) {
player.printError("No block in sight!"); player.printError("No block in sight!");

View File

@ -23,6 +23,7 @@ import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Direction;
@ -210,6 +211,17 @@ public interface Player extends Entity, Actor {
*/ */
Location getBlockTrace(int range, boolean useLastBlock); Location getBlockTrace(int range, boolean useLastBlock);
/**
* Get the point of the block being looked at. May return null.
* Will return the farthest away block before matching the stop mask if useLastBlock is true and no other block is found.
*
* @param range how far to checks for blocks
* @param useLastBlock try to return the last valid block not matching the stop mask found
* @param stopMask the mask used to determine when to stop tracing
* @return point
*/
Location getBlockTrace(int range, boolean useLastBlock, @Nullable Mask stopMask);
/** /**
* Get the face that the player is looking at. * Get the face that the player is looking at.
* *
@ -219,6 +231,16 @@ public interface Player extends Entity, Actor {
*/ */
Location getBlockTraceFace(int range, boolean useLastBlock); Location getBlockTraceFace(int range, boolean useLastBlock);
/**
* Get the face that the player is looking at.
*
* @param range the range
* @param useLastBlock try to return the last valid block not matching the stop mask found
* @param stopMask the mask used to determine when to stop tracing
* @return a face
*/
Location getBlockTraceFace(int range, boolean useLastBlock, @Nullable Mask stopMask);
/** /**
* Get the point of the block being looked at. May return null. * Get the point of the block being looked at. May return null.
* *

View File

@ -23,6 +23,7 @@ import com.sk89q.worldedit.NotABlockException;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.internal.cui.CUIEvent;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.Vector3;
@ -41,6 +42,7 @@ import com.sk89q.worldedit.world.gamemode.GameModes;
import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.item.ItemTypes;
import javax.annotation.Nullable;
import java.io.File; import java.io.File;
/** /**
@ -323,13 +325,29 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
@Override @Override
public Location getBlockTrace(int range, boolean useLastBlock) { public Location getBlockTrace(int range, boolean useLastBlock) {
TargetBlock tb = new TargetBlock(this, range, 0.2); return getBlockTrace(range, useLastBlock, null);
return (useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock());
} }
@Override @Override
public Location getBlockTraceFace(int range, boolean useLastBlock) { public Location getBlockTraceFace(int range, boolean useLastBlock) {
return getBlockTraceFace(range, useLastBlock, null);
}
@Override
public Location getBlockTrace(int range, boolean useLastBlock, @Nullable Mask stopMask) {
TargetBlock tb = new TargetBlock(this, range, 0.2); TargetBlock tb = new TargetBlock(this, range, 0.2);
if (stopMask != null) {
tb.setStopMask(stopMask);
}
return (useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock());
}
@Override
public Location getBlockTraceFace(int range, boolean useLastBlock, @Nullable Mask stopMask) {
TargetBlock tb = new TargetBlock(this, range, 0.2);
if (stopMask != null) {
tb.setStopMask(stopMask);
}
return (useLastBlock ? tb.getAnyTargetBlockFace() : tb.getTargetBlockFace()); return (useLastBlock ? tb.getAnyTargetBlockFace() : tb.getTargetBlockFace());
} }

View File

@ -45,7 +45,8 @@ public class RequestExtent implements Extent {
if (request == null || !request.isValid()) { if (request == null || !request.isValid()) {
request = Request.request(); request = Request.request();
} }
return request.getEditSession(); final EditSession editSession = request.getEditSession();
return editSession == null ? request.getWorld() : editSession;
} }
@Override @Override

View File

@ -20,10 +20,15 @@
package com.sk89q.worldedit.util; package com.sk89q.worldedit.util;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.SolidBlockMask;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import javax.annotation.Nullable;
/** /**
* This class uses an inefficient method to figure out what block a player * This class uses an inefficient method to figure out what block a player
* is looking towards. * is looking towards.
@ -33,7 +38,8 @@ import com.sk89q.worldedit.world.World;
*/ */
public class TargetBlock { public class TargetBlock {
private World world; private final World world;
private int maxDistance; private int maxDistance;
private double checkDistance, curDistance; private double checkDistance, curDistance;
private BlockVector3 targetPos = BlockVector3.ZERO; private BlockVector3 targetPos = BlockVector3.ZERO;
@ -41,6 +47,11 @@ public class TargetBlock {
private BlockVector3 prevPos = BlockVector3.ZERO; private BlockVector3 prevPos = BlockVector3.ZERO;
private Vector3 offset = Vector3.ZERO; private Vector3 offset = Vector3.ZERO;
// the mask which dictates when to stop a trace - defaults to stopping at non-air blocks
private Mask stopMask;
// the mask which dictates when to stop a solid block trace - default to BlockMaterial#isMovementBlocker
private Mask solidMask;
/** /**
* Constructor requiring a player, uses default values * Constructor requiring a player, uses default values
* *
@ -50,6 +61,8 @@ public class TargetBlock {
this.world = player.getWorld(); this.world = player.getWorld();
this.setValues(player.getLocation().toVector(), player.getLocation().getYaw(), player.getLocation().getPitch(), this.setValues(player.getLocation().toVector(), player.getLocation().getYaw(), player.getLocation().getPitch(),
300, 1.65, 0.2); 300, 1.65, 0.2);
this.stopMask = new ExistingBlockMask(world);
this.solidMask = new SolidBlockMask(world);
} }
/** /**
@ -62,6 +75,36 @@ public class TargetBlock {
public TargetBlock(Player player, int maxDistance, double checkDistance) { public TargetBlock(Player player, int maxDistance, double checkDistance) {
this.world = player.getWorld(); this.world = player.getWorld();
this.setValues(player.getLocation().toVector(), player.getLocation().getYaw(), player.getLocation().getPitch(), maxDistance, 1.65, checkDistance); this.setValues(player.getLocation().toVector(), player.getLocation().getYaw(), player.getLocation().getPitch(), maxDistance, 1.65, checkDistance);
this.stopMask = new ExistingBlockMask(world);
this.solidMask = new SolidBlockMask(world);
}
/**
* Set the mask used for determine where to stop traces.
* Setting to null will restore the default.
*
* @param stopMask the mask used to stop traces
*/
public void setStopMask(@Nullable Mask stopMask) {
if (stopMask == null) {
this.stopMask = new ExistingBlockMask(world);
} else {
this.stopMask = stopMask;
}
}
/**
* Set the mask used for determine where to stop solid block traces.
* Setting to null will restore the default.
*
* @param solidMask the mask used to stop solid block traces
*/
public void setSolidMask(@Nullable Mask solidMask) {
if (solidMask == null) {
this.solidMask = new SolidBlockMask(world);
} else {
this.solidMask = solidMask;
}
} }
/** /**
@ -79,7 +122,7 @@ public class TargetBlock {
this.checkDistance = checkDistance; this.checkDistance = checkDistance;
this.curDistance = 0; this.curDistance = 0;
xRotation = (xRotation + 90) % 360; xRotation = (xRotation + 90) % 360;
yRotation = yRotation * -1; yRotation *= -1;
double h = (checkDistance * Math.cos(Math.toRadians(yRotation))); double h = (checkDistance * Math.cos(Math.toRadians(yRotation)));
@ -102,15 +145,15 @@ public class TargetBlock {
boolean searchForLastBlock = true; boolean searchForLastBlock = true;
Location lastBlock = null; Location lastBlock = null;
while (getNextBlock() != null) { while (getNextBlock() != null) {
if (world.getBlock(targetPos).getBlockType().getMaterial().isAir()) { if (stopMask.test(targetPos)) {
break;
} else {
if (searchForLastBlock) { if (searchForLastBlock) {
lastBlock = getCurrentBlock(); lastBlock = getCurrentBlock();
if (lastBlock.getBlockY() <= 0 || lastBlock.getBlockY() >= world.getMaxY()) { if (lastBlock.getBlockY() <= 0 || lastBlock.getBlockY() >= world.getMaxY()) {
searchForLastBlock = false; searchForLastBlock = false;
} }
} }
} else {
break;
} }
} }
Location currentBlock = getCurrentBlock(); Location currentBlock = getCurrentBlock();
@ -124,7 +167,8 @@ public class TargetBlock {
* @return Block * @return Block
*/ */
public Location getTargetBlock() { public Location getTargetBlock() {
while (getNextBlock() != null && world.getBlock(targetPos).getBlockType().getMaterial().isAir()) ; //noinspection StatementWithEmptyBody
while (getNextBlock() != null && !stopMask.test(targetPos)) ;
return getCurrentBlock(); return getCurrentBlock();
} }
@ -135,7 +179,8 @@ public class TargetBlock {
* @return Block * @return Block
*/ */
public Location getSolidTargetBlock() { public Location getSolidTargetBlock() {
while (getNextBlock() != null && !world.getBlock(targetPos).getBlockType().getMaterial().isMovementBlocker()) ; //noinspection StatementWithEmptyBody
while (getNextBlock() != null && !solidMask.test(targetPos)) ;
return getCurrentBlock(); return getCurrentBlock();
} }