872 lines
41 KiB
Java
872 lines
41 KiB
Java
/*
|
|
* WorldEdit, a Minecraft world manipulation toolkit
|
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
|
* Copyright (C) WorldEdit team and contributors
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by the
|
|
* Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package com.sk89q.worldedit.command;
|
|
|
|
import com.boydti.fawe.Fawe;
|
|
import com.boydti.fawe.FaweCache;
|
|
import com.boydti.fawe.config.BBC;
|
|
import com.boydti.fawe.config.Settings;
|
|
import com.boydti.fawe.object.FaweLimit;
|
|
import com.boydti.fawe.object.FawePlayer;
|
|
import com.boydti.fawe.object.brush.*;
|
|
import com.boydti.fawe.object.brush.heightmap.ScalableHeightMap;
|
|
import com.boydti.fawe.object.brush.sweep.SweepBrush;
|
|
import com.boydti.fawe.object.clipboard.MultiClipboardHolder;
|
|
import com.boydti.fawe.object.mask.IdMask;
|
|
import com.boydti.fawe.util.ColorUtil;
|
|
import com.boydti.fawe.util.MathMan;
|
|
import com.boydti.fawe.util.image.ImageUtil;
|
|
import com.sk89q.minecraft.util.commands.*;
|
|
import com.sk89q.worldedit.*;
|
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
|
import com.sk89q.worldedit.blocks.BlockID;
|
|
import com.sk89q.worldedit.command.tool.BrushTool;
|
|
import com.sk89q.worldedit.command.tool.InvalidToolBindException;
|
|
import com.sk89q.worldedit.command.tool.brush.*;
|
|
import com.sk89q.worldedit.command.util.CreatureButcher;
|
|
import com.sk89q.worldedit.entity.Player;
|
|
import com.sk89q.worldedit.extension.input.ParserContext;
|
|
import com.sk89q.worldedit.extension.platform.Actor;
|
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
|
|
import com.sk89q.worldedit.function.mask.BlockMask;
|
|
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
|
|
import com.sk89q.worldedit.function.mask.Mask;
|
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
|
import com.sk89q.worldedit.internal.expression.Expression;
|
|
import com.sk89q.worldedit.internal.expression.runtime.Constant;
|
|
import com.sk89q.worldedit.internal.expression.runtime.RValue;
|
|
import com.sk89q.worldedit.session.ClipboardHolder;
|
|
import com.sk89q.worldedit.util.command.InvalidUsageException;
|
|
import com.sk89q.worldedit.util.command.binding.Range;
|
|
import com.sk89q.worldedit.util.command.binding.Switch;
|
|
import com.sk89q.worldedit.util.command.parametric.Optional;
|
|
import com.sk89q.worldedit.util.command.parametric.ParameterException;
|
|
|
|
import java.awt.Color;
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.*;
|
|
import java.net.URI;
|
|
import java.net.URL;
|
|
import java.nio.channels.Channels;
|
|
import java.nio.channels.ReadableByteChannel;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Commands to set brush shape.
|
|
*/
|
|
@Command(aliases = {"brush", "br", "tool"},
|
|
desc = "Commands to build and draw from far away. [More Info](https://git.io/vSPYf)"
|
|
)
|
|
public class BrushCommands extends BrushProcessor {
|
|
|
|
public BrushCommands(WorldEdit worldEdit) {
|
|
super(worldEdit);
|
|
}
|
|
|
|
public static void checkMaxBrushRadius(Expression radius) throws MaxBrushRadiusException {
|
|
if (WorldEdit.getInstance().getConfiguration().maxBrushRadius > 0) {
|
|
RValue r = radius.getRoot();
|
|
if (r instanceof Constant && ((Constant) r).getValue() > WorldEdit.getInstance().getConfiguration().maxBrushRadius) {
|
|
throw new MaxBrushRadiusException();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"blendball", "bb", "blend"},
|
|
usage = "[radius=5]",
|
|
desc = "Smooths and blends terrain",
|
|
help = "Smooths and blends terrain\n" +
|
|
"Pic: https://i.imgur.com/cNUQUkj.png -> https://i.imgur.com/hFOFsNf.png",
|
|
min = 0,
|
|
max = 1
|
|
)
|
|
@CommandPermissions("worldedit.brush.blendball")
|
|
public BrushSettings blendBallBrush(Player player, LocalSession session, @Optional("5") Expression radius, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context, new BlendBall()).setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"erode", "e"},
|
|
usage = "[radius=5]",
|
|
desc = "Erodes terrain",
|
|
help = "Erodes terrain",
|
|
min = 0,
|
|
max = 1
|
|
)
|
|
@CommandPermissions("worldedit.brush.erode")
|
|
public BrushSettings erodeBrush(Player player, LocalSession session, @Optional("5") Expression radius, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context, new ErodeBrush()).setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"pull"},
|
|
usage = "[radius=5]",
|
|
desc = "Pull terrain towards you",
|
|
help = "Pull terrain towards you",
|
|
min = 0,
|
|
max = 1
|
|
)
|
|
@CommandPermissions("worldedit.brush.pull")
|
|
public BrushSettings pullBrush(Player player, LocalSession session, @Optional("5") Expression radius, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context, new RaiseBrush()).setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"circle"},
|
|
usage = "<pattern> [radius=5]",
|
|
desc = "Creates a circle which revolves around your facing direction",
|
|
help = "Creates a circle which revolves around your facing direction.\n" +
|
|
"Note: Decrease brush radius, and enabled visualization to assist with placement mid-air",
|
|
min = 1,
|
|
max = 2
|
|
)
|
|
@CommandPermissions("worldedit.brush.sphere")
|
|
public BrushSettings circleBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") Expression radius, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context, new CircleBrush(player)).setSize(radius).setFill(fill);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"recursive", "recurse", "r"},
|
|
usage = "<pattern-to> [radius=5]",
|
|
desc = "Set all connected blocks",
|
|
help = "Set all connected blocks\n" +
|
|
"The -d flag Will apply in depth first order\n" +
|
|
"Note: Set a mask to recurse along specific blocks",
|
|
min = 0,
|
|
max = 3
|
|
)
|
|
@CommandPermissions("worldedit.brush.recursive")
|
|
public BrushSettings recursiveBrush(Player player, LocalSession session, EditSession editSession, Pattern fill, @Optional("5") Expression radius, @Switch('d') boolean depthFirst, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context,
|
|
new RecurseBrush(depthFirst))
|
|
.setSize(radius)
|
|
.setFill(fill)
|
|
.setMask(new IdMask(editSession));
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"line", "l"},
|
|
usage = "<pattern> [radius=0]",
|
|
flags = "hsf",
|
|
desc = "Create lines",
|
|
help =
|
|
"Create lines.\n" +
|
|
"The -h flag creates only a shell\n" +
|
|
"The -s flag selects the clicked point after drawing\n" +
|
|
"The -f flag creates a flat line",
|
|
min = 1,
|
|
max = 2
|
|
)
|
|
@CommandPermissions("worldedit.brush.line")
|
|
public BrushSettings lineBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("0") Expression radius, @Switch('h') boolean shell, @Switch('s') boolean select, @Switch('f') boolean flat, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context,
|
|
new LineBrush(shell, select, flat))
|
|
.setSize(radius)
|
|
.setFill(fill);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"spline", "spl", "curve"},
|
|
usage = "<pattern>",
|
|
desc = "Join multiple objects together in a curve",
|
|
help = "Click to select some objects,click the same block twice to connect the objects.\n" +
|
|
"Insufficient brush radius, or clicking the the wrong spot will result in undesired shapes. The shapes must be simple lines or loops.\n" +
|
|
"Pic1: http://i.imgur.com/CeRYAoV.jpg -> http://i.imgur.com/jtM0jA4.png\n" +
|
|
"Pic2: http://i.imgur.com/bUeyc72.png -> http://i.imgur.com/tg6MkcF.png" +
|
|
"Tutorial: https://www.planetminecraft.com/blog/fawe-tutorial/",
|
|
min = 0,
|
|
max = 2
|
|
)
|
|
@CommandPermissions("worldedit.brush.spline")
|
|
public BrushSettings splineBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("25") Expression radius, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
player.print(BBC.getPrefix() + BBC.BRUSH_SPLINE.f(radius));
|
|
return set(session, context,
|
|
new SplineBrush(player, session))
|
|
.setSize(radius)
|
|
.setFill(fill);
|
|
}
|
|
|
|
// Adapted from: https://github.com/Rafessor/VaeronTools
|
|
@Command(
|
|
aliases = {"sweep", "sw", "vaesweep"},
|
|
usage = "[copies=-1]",
|
|
desc = "Sweep your clipboard content along a curve",
|
|
help = "Sweeps your clipboard content along a curve.\n" +
|
|
"Define a curve by selecting the individual points with a brush\n" +
|
|
"Set [copies] to a value > 0 if you want to have your selection pasted a limited amount of times equally spaced on the curve",
|
|
max = 1
|
|
)
|
|
@CommandPermissions("worldedit.brush.sweep")
|
|
public BrushSettings sweepBrush(Player player, LocalSession session, EditSession editSession, @Optional("-1") int copies, CommandContext context) throws WorldEditException {
|
|
player.print(BBC.getPrefix() + BBC.BRUSH_SPLINE.s());
|
|
return set(session, context, new SweepBrush(copies));
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"catenary", "cat", "gravityline", "saggedline"},
|
|
usage = "<pattern> [lengthFactor=1.2] [size=0]",
|
|
desc = "Create a hanging line between two points",
|
|
help = "Create a hanging line between two points.\n" +
|
|
"The lengthFactor controls how long the line is\n" +
|
|
"The -h flag creates only a shell\n" +
|
|
"The -s flag selects the clicked point after drawing\n" +
|
|
"The -d flag sags the catenary toward the facing direction\n",
|
|
min = 1,
|
|
max = 3
|
|
)
|
|
@CommandPermissions("worldedit.brush.spline")
|
|
public BrushSettings catenaryBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("1.2") @Range(min=1) double lengthFactor, @Optional("0") Expression radius, @Switch('h') boolean shell, @Switch('s') boolean select, @Switch('d') boolean facingDirection, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context,
|
|
new CatenaryBrush(shell, select, facingDirection, lengthFactor))
|
|
.setSize(radius)
|
|
.setFill(fill);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"sspl", "sspline", "surfacespline"},
|
|
usage = "<pattern> [size=0] [tension=0] [bias=0] [continuity=0] [quality=10]",
|
|
desc = "Draws a spline (curved line) on the surface",
|
|
help = "Create a spline on the surface\n" +
|
|
"Video: https://www.youtube.com/watch?v=zSN-2jJxXlM",
|
|
min = 0,
|
|
max = 6
|
|
)
|
|
@CommandPermissions("worldedit.brush.surfacespline") // 0, 0, 0, 10, 0,
|
|
public BrushSettings surfaceSpline(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("0") Expression radius, @Optional("0") double tension, @Optional("0") double bias, @Optional("0") double continuity, @Optional("10") double quality, CommandContext context) throws WorldEditException {
|
|
player.print(BBC.getPrefix() + BBC.BRUSH_SPLINE.f(radius));
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context,
|
|
new SurfaceSpline(tension, bias, continuity, quality))
|
|
.setSize(radius)
|
|
.setFill(fill);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"rock", "blob"},
|
|
usage = "<pattern> [radius=10] [roundness=100] [frequency=30] [amplitude=50]",
|
|
flags = "h",
|
|
desc = "Creates a distorted sphere",
|
|
min = 1,
|
|
max = 5
|
|
)
|
|
@CommandPermissions("worldedit.brush.rock")
|
|
public BrushSettings blobBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("10") Vector radius, @Optional("100") double sphericity, @Optional("30") double frequency, @Optional("50") double amplitude, CommandContext context) throws WorldEditException {
|
|
double max = MathMan.max(radius.getBlockX(), radius.getBlockY(), radius.getBlockZ());
|
|
WorldEdit.getInstance().checkMaxBrushRadius(max);
|
|
Brush brush = new BlobBrush(radius.divide(max), frequency / 100, amplitude / 100, sphericity / 100);
|
|
return set(session, context,
|
|
brush)
|
|
.setSize(max)
|
|
.setFill(fill);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"sphere", "s"},
|
|
usage = "<pattern> [radius=2]",
|
|
flags = "hf",
|
|
desc = "Creates a sphere",
|
|
help =
|
|
"Creates a sphere.\n" +
|
|
"The -h flag creates hollow spheres instead." +
|
|
"The -f flag creates falling spheres.",
|
|
min = 1,
|
|
max = 2
|
|
)
|
|
@CommandPermissions("worldedit.brush.sphere")
|
|
public BrushSettings sphereBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("2") @Range(min=0) Expression radius, @Switch('h') boolean hollow, @Switch('f') boolean falling, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
|
|
Brush brush;
|
|
if (hollow) {
|
|
brush = new HollowSphereBrush();
|
|
} else {
|
|
if (fill instanceof BaseBlock) {
|
|
BaseBlock block = (BaseBlock) fill;
|
|
switch (block.getId()) {
|
|
case BlockID.SAND:
|
|
case BlockID.GRAVEL:
|
|
BBC.BRUSH_TRY_OTHER.send(player);
|
|
falling = true;
|
|
break;
|
|
}
|
|
}
|
|
if (falling) {
|
|
brush = new FallingSphere();
|
|
} else {
|
|
brush = new SphereBrush();
|
|
}
|
|
|
|
}
|
|
return set(session, context,
|
|
brush)
|
|
.setSize(radius)
|
|
.setFill(fill);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"shatter", "partition", "split"},
|
|
usage = "<pattern> [radius=10] [count=10]",
|
|
desc = "Creates random lines to break the terrain into pieces",
|
|
help =
|
|
"Creates uneven lines separating terrain into multiple pieces\n" +
|
|
"Pic: https://i.imgur.com/2xKsZf2.png",
|
|
min = 1,
|
|
max = -1
|
|
)
|
|
@CommandPermissions("worldedit.brush.shatter")
|
|
public BrushSettings shatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("10") Expression radius, @Optional("10") int count, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context,
|
|
new ShatterBrush(count))
|
|
.setSize(radius)
|
|
.setFill(fill)
|
|
.setMask(new ExistingBlockMask(editSession));
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"stencil"},
|
|
usage = "<pattern> [radius=5] [file|#clipboard|imgur=null] [rotation=360] [yscale=1.0]",
|
|
desc = "Use a height map to paint a surface",
|
|
help =
|
|
"Use a height map to paint any surface.\n" +
|
|
"The -w flag will only apply at maximum saturation\n" +
|
|
"The -r flag will apply random rotation",
|
|
min = 1,
|
|
max = -1
|
|
)
|
|
@CommandPermissions("worldedit.brush.stencil")
|
|
public BrushSettings stencilBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") Expression radius, @Optional() final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('w') boolean onlyWhite, @Switch('r') boolean randomRotate, CommandContext context) throws WorldEditException, FileNotFoundException, ParameterException {
|
|
checkMaxBrushRadius(radius);
|
|
InputStream stream = getHeightmapStream(image);
|
|
HeightBrush brush;
|
|
try {
|
|
brush = new StencilBrush(stream, rotation, yscale, onlyWhite, "#clipboard".equalsIgnoreCase(image) ? session.getClipboard().getClipboard() : null);
|
|
} catch (EmptyClipboardException ignore) {
|
|
brush = new StencilBrush(stream, rotation, yscale, onlyWhite, null);
|
|
}
|
|
if (randomRotate) {
|
|
brush.setRandomRotate(true);
|
|
}
|
|
return set(session, context,
|
|
brush)
|
|
.setSize(radius)
|
|
.setFill(fill);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"image", "color"},
|
|
usage = "<radius> <image> [yscale=1]",
|
|
desc = "Use a height map to paint a surface",
|
|
flags = "a",
|
|
help =
|
|
"Use a height map to paint any surface.\n" +
|
|
"The -a flag will use image alpha\n" +
|
|
"The -f blends the image with the existing terrain",
|
|
min = 1,
|
|
max = -1
|
|
)
|
|
@CommandPermissions("worldedit.brush.stencil")
|
|
public BrushSettings imageBrush(Player player, EditSession editSession, LocalSession session, @Optional("5") Expression radius, BufferedImage image, @Optional("1") @Range(min=Double.MIN_NORMAL) final double yscale, @Switch('a') boolean alpha, @Switch('f') boolean fadeOut, CommandContext context) throws WorldEditException, IOException {
|
|
checkMaxBrushRadius(radius);
|
|
if (yscale != 1) {
|
|
ImageUtil.scaleAlpha(image, yscale);
|
|
alpha = true;
|
|
}
|
|
if (fadeOut) {
|
|
ImageUtil.fadeAlpha(image);
|
|
alpha = true;
|
|
}
|
|
ImageBrush brush = new ImageBrush(image, session, alpha);
|
|
return set(session, context,
|
|
brush)
|
|
.setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"surface", "surf"},
|
|
usage = "<pattern> [radius=5]",
|
|
desc = "Use a height map to paint a surface",
|
|
help =
|
|
"Use a height map to paint any surface.\n" +
|
|
"The -w flag will only apply at maximum saturation\n" +
|
|
"The -r flag will apply random rotation",
|
|
min = 1,
|
|
max = -1
|
|
)
|
|
@CommandPermissions("worldedit.brush.surface")
|
|
public BrushSettings surfaceBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") Expression radius, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context, new SurfaceSphereBrush()).setFill(fill).setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"scatter", "scat"},
|
|
usage = "<pattern> [radius=5] [points=5] [distance=1]",
|
|
desc = "Scatter a pattern on a surface",
|
|
help =
|
|
"Set a number of blocks randomly on a surface each a certain distance apart.\n" +
|
|
" The -o flag will overlay the block\n" +
|
|
"Video: https://youtu.be/RPZIaTbqoZw?t=34s",
|
|
flags = "o",
|
|
min = 1,
|
|
max = 4
|
|
)
|
|
@CommandPermissions("worldedit.brush.scatter")
|
|
public BrushSettings scatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") Expression radius, @Optional("5") double points, @Optional("1") double distance, @Switch('o') boolean overlay, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
Brush brush;
|
|
if (overlay) {
|
|
brush = new ScatterOverlayBrush((int) points, (int) distance);
|
|
} else {
|
|
brush = new ScatterBrush((int) points, (int) distance);
|
|
}
|
|
return set(session, context,
|
|
brush)
|
|
.setSize(radius)
|
|
.setFill(fill);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"populateschematic", "populateschem", "popschem", "pschem", "ps"},
|
|
usage = "<mask> <file|folder|url> [radius=30] [points=5]",
|
|
desc = "Scatter a schematic on a surface",
|
|
help =
|
|
"Chooses the scatter schematic brush.\n" +
|
|
"The -r flag will apply random rotation",
|
|
flags = "r",
|
|
min = 2,
|
|
max = 4
|
|
)
|
|
@CommandPermissions("worldedit.brush.populateschematic")
|
|
public BrushSettings scatterSchemBrush(Player player, EditSession editSession, LocalSession session, Mask mask, String clipboard, @Optional("30") Expression radius, @Optional("50") double density, @Switch('r') boolean rotate, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
|
|
|
|
try {
|
|
MultiClipboardHolder clipboards = ClipboardFormat.SCHEMATIC.loadAllFromInput(player, player.getWorld().getWorldData(), clipboard, true);
|
|
if (clipboards == null) {
|
|
BBC.SCHEMATIC_NOT_FOUND.send(player, clipboard);
|
|
return null;
|
|
}
|
|
List<ClipboardHolder> holders = clipboards.getHolders();
|
|
if (holders == null) {
|
|
BBC.SCHEMATIC_NOT_FOUND.send(player, clipboard);
|
|
return null;
|
|
}
|
|
|
|
return set(session, context,
|
|
new PopulateSchem(mask, holders, (int) density, rotate))
|
|
.setSize(radius);
|
|
} catch (IOException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"layer"},
|
|
usage = "<radius> [color|<pattern1> <patern2>...]",
|
|
desc = "Replaces terrain with a layer.",
|
|
help = "Replaces terrain with a layer.\n" +
|
|
"Example: /br layer 5 95:1 95:2 35:15 - Places several layers on a surface\n" +
|
|
"Pic: https://i.imgur.com/XV0vYoX.png",
|
|
min = 0,
|
|
max = 999
|
|
)
|
|
@CommandPermissions("worldedit.brush.layer")
|
|
public BrushSettings surfaceLayer(Player player, EditSession editSession, LocalSession session, Expression radius, CommandContext args, CommandContext context) throws WorldEditException, InvalidUsageException {
|
|
checkMaxBrushRadius(radius);
|
|
ParserContext parserContext = new ParserContext();
|
|
parserContext.setActor(player);
|
|
parserContext.setWorld(player.getWorld());
|
|
parserContext.setSession(session);
|
|
parserContext.setExtent(editSession);
|
|
List<BaseBlock> blocks = new ArrayList<>();
|
|
if (args.argsLength() < 2) {
|
|
throw new InvalidUsageException(getCallable());
|
|
}
|
|
try {
|
|
Color color = ColorUtil.parseColor(args.getString(1));
|
|
char[] glassLayers = Fawe.get().getTextureUtil().getNearestLayer(color.getRGB());
|
|
for (char layer : glassLayers) {
|
|
blocks.add(FaweCache.CACHE_BLOCK[layer]);
|
|
}
|
|
} catch (IllegalArgumentException ignore) {
|
|
for (int i = 1; i < args.argsLength(); i++) {
|
|
String arg = args.getString(i);
|
|
blocks.add(getWorldEdit().getBlockFactory().parseFromInput(arg, parserContext));
|
|
}
|
|
}
|
|
return set(session, context,
|
|
new LayerBrush(blocks.toArray(new BaseBlock[blocks.size()])))
|
|
.setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"splatter", "splat"},
|
|
usage = "<pattern> [radius=5] [seeds=1] [recursion=5] [solid=true]",
|
|
desc = "Splatter a pattern on a surface",
|
|
help = "Sets a bunch of blocks randomly on a surface.\n" +
|
|
"Pic: https://i.imgur.com/hMD29oO.png\n" +
|
|
"Example: /br splatter stone,dirt 30 15\n" +
|
|
"Note: The seeds define how many splotches there are, recursion defines how large, solid defines whether the pattern is applied per seed, else per block.",
|
|
min = 1,
|
|
max = 5
|
|
)
|
|
@CommandPermissions("worldedit.brush.splatter")
|
|
public BrushSettings splatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") Expression radius, @Optional("1") double points, @Optional("5") double recursion, @Optional("true") boolean solid, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context,
|
|
new SplatterBrush((int) points, (int) recursion, solid))
|
|
.setSize(radius)
|
|
.setFill(fill);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"scmd", "scattercmd", "scattercommand", "scommand"},
|
|
usage = "<scatter-radius> <points> <cmd-radius=1> <cmd1;cmd2...>",
|
|
desc = "Run commands at random points on a surface",
|
|
help =
|
|
"Run commands at random points on a surface\n" +
|
|
" - The scatter radius is the min distance between each point\n" +
|
|
" - Your selection will be expanded to the specified size around each point\n" +
|
|
" - Placeholders: {x}, {y}, {z}, {world}, {size}",
|
|
min = 1,
|
|
max = -1
|
|
)
|
|
@CommandPermissions("worldedit.brush.scattercommand")
|
|
public BrushSettings scatterCommandBrush(Player player, EditSession editSession, LocalSession session, Expression radius, double points, double distance, CommandContext args, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
return set(session, context,
|
|
new ScatterCommand((int) points, (int) distance, args.getJoinedStrings(3)))
|
|
.setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"cylinder", "cyl", "c", "disk", "disc"},
|
|
usage = "<pattern> [radius=2] [height=1]",
|
|
flags = "h",
|
|
desc = "Creates a cylinder",
|
|
help =
|
|
"Creates a cylinder.\n" +
|
|
"The -h flag creates hollow cylinders instead.",
|
|
min = 1,
|
|
max = 3
|
|
)
|
|
@CommandPermissions("worldedit.brush.cylinder")
|
|
public BrushSettings cylinderBrush(Player player, EditSession editSession, LocalSession session, Pattern fill,
|
|
@Optional("2") Expression radius, @Optional("1") int height, @Switch('h') boolean hollow, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
WorldEdit.getInstance().checkMaxBrushRadius(height);
|
|
|
|
BrushSettings settings;
|
|
if (hollow) {
|
|
settings = set(session, context, new HollowCylinderBrush(height));
|
|
} else {
|
|
settings = set(session, context, new CylinderBrush(height));
|
|
}
|
|
settings.setSize(radius)
|
|
.setFill(fill);
|
|
return settings;
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"clipboard"},
|
|
usage = "",
|
|
desc = "Choose the clipboard brush (Recommended: `/br copypaste`)",
|
|
help =
|
|
"Chooses the clipboard brush.\n" +
|
|
"The -a flag makes it not paste air.\n" +
|
|
"Without the -p flag, the paste will appear centered at the target location. " +
|
|
"With the flag, then the paste will appear relative to where you had " +
|
|
"stood relative to the copied area when you copied it."
|
|
)
|
|
@CommandPermissions("worldedit.brush.clipboard")
|
|
public BrushSettings clipboardBrush(Player player, LocalSession session, @Switch('a') boolean ignoreAir, @Switch('p') boolean usingOrigin, CommandContext context) throws WorldEditException {
|
|
ClipboardHolder holder = session.getClipboard();
|
|
Clipboard clipboard = holder.getClipboard();
|
|
|
|
Vector size = clipboard.getDimensions();
|
|
|
|
WorldEdit.getInstance().checkMaxBrushRadius(size.getBlockX());
|
|
WorldEdit.getInstance().checkMaxBrushRadius(size.getBlockY());
|
|
WorldEdit.getInstance().checkMaxBrushRadius(size.getBlockZ());
|
|
return set(session, context, new ClipboardBrush(holder, ignoreAir, usingOrigin));
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"smooth"},
|
|
usage = "[size=2] [iterations=4]",
|
|
flags = "n",
|
|
desc = "Smooths terrain (Recommended: `/br blendball`)",
|
|
help =
|
|
"Chooses the terrain softener brush.\n" +
|
|
"The -n flag makes it only consider naturally occurring blocks.",
|
|
min = 0,
|
|
max = 2
|
|
)
|
|
@CommandPermissions("worldedit.brush.smooth")
|
|
public BrushSettings smoothBrush(Player player, LocalSession session, EditSession editSession,
|
|
@Optional("2") Expression radius, @Optional("4") int iterations, @Switch('n')
|
|
boolean naturalBlocksOnly, CommandContext context) throws WorldEditException {
|
|
|
|
checkMaxBrushRadius(radius);
|
|
|
|
FawePlayer fp = FawePlayer.wrap(player);
|
|
FaweLimit limit = Settings.IMP.getLimit(fp);
|
|
iterations = Math.min(limit.MAX_ITERATIONS, iterations);
|
|
|
|
return set(session, context,
|
|
new SmoothBrush(iterations, naturalBlocksOnly))
|
|
.setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"ex", "extinguish"},
|
|
usage = "[radius=5]",
|
|
desc = "Shortcut fire extinguisher brush",
|
|
min = 0,
|
|
max = 1
|
|
)
|
|
@CommandPermissions("worldedit.brush.ex")
|
|
public BrushSettings extinguishBrush(Player player, LocalSession session, EditSession editSession, @Optional("5") Expression radius, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
|
|
Pattern fill = (new BaseBlock(0));
|
|
return set(session, context,
|
|
new SphereBrush())
|
|
.setSize(radius)
|
|
.setFill(fill)
|
|
.setMask(new BlockMask(editSession, new BaseBlock(BlockID.FIRE)));
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"gravity", "grav"},
|
|
usage = "[radius=5]",
|
|
flags = "h",
|
|
desc = "Gravity brush",
|
|
help =
|
|
"This brush simulates the affect of gravity.\n" +
|
|
"The -h flag makes it affect blocks starting at the world's max y, " +
|
|
"instead of the clicked block's y + radius.",
|
|
min = 0,
|
|
max = 1
|
|
)
|
|
@CommandPermissions("worldedit.brush.gravity")
|
|
public BrushSettings gravityBrush(Player player, LocalSession session, @Optional("5") Expression radius, @Switch('h') boolean fromMaxY, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
|
|
return set(session, context,
|
|
new GravityBrush(fromMaxY))
|
|
.setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"height", "heightmap"},
|
|
usage = "[radius=5] [file|#clipboard|imgur=null] [rotation=0] [yscale=1.00]",
|
|
flags = "h",
|
|
desc = "Raise or lower terrain using a heightmap",
|
|
help =
|
|
"This brush raises and lowers land.\n" +
|
|
" - The `-r` flag enables random off-axis rotation\n" +
|
|
" - The `-l` flag will work on snow layers\n" +
|
|
" - The `-s` flag disables smoothing\n" +
|
|
"Note: Use a negative yscale to reduce height\n" +
|
|
"Snow Pic: https://i.imgur.com/Hrzn0I4.png",
|
|
min = 1,
|
|
max = 4
|
|
)
|
|
@CommandPermissions("worldedit.brush.height")
|
|
public BrushSettings heightBrush(Player player, LocalSession session, @Optional("5") Expression radius, @Optional() final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException, FileNotFoundException, ParameterException {
|
|
return terrainBrush(player, session, radius, image, rotation, yscale, false, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"cliff", "flatcylinder"},
|
|
usage = "[radius=5] [file|#clipboard|imgur=null] [rotation=0] [yscale=1.00]",
|
|
flags = "h",
|
|
desc = "Cliff brush",
|
|
help =
|
|
"This brush flattens terrain and creates cliffs.\n" +
|
|
" - The `-r` flag enables random off-axis rotation\n" +
|
|
" - The `-l` flag will work on snow layers\n" +
|
|
" - The `-s` flag disables smoothing",
|
|
min = 1,
|
|
max = 4
|
|
)
|
|
@CommandPermissions("worldedit.brush.height")
|
|
public BrushSettings cliffBrush(Player player, LocalSession session, @Optional("5") Expression radius, @Optional() final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException, FileNotFoundException, ParameterException {
|
|
return terrainBrush(player, session, radius, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CYLINDER, context);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"flatten", "flatmap", "flat"},
|
|
usage = "[radius=5] [file|#clipboard|imgur=null] [rotation=0] [yscale=1.00]",
|
|
flags = "h",
|
|
help = "Flatten brush flattens terrain\n" +
|
|
" - The `-r` flag enables random off-axis rotation\n" +
|
|
" - The `-l` flag will work on snow layers\n" +
|
|
" - The `-s` flag disables smoothing",
|
|
desc = "This brush raises or lowers land towards the clicked point",
|
|
min = 1,
|
|
max = 4
|
|
)
|
|
@CommandPermissions("worldedit.brush.height")
|
|
public BrushSettings flattenBrush(Player player, LocalSession session, @Optional("5") Expression radius, @Optional() final String image, @Optional("0") @Step(90) @Range(min=0, max=360) final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers, @Switch('s') boolean dontSmooth, CommandContext context) throws WorldEditException, FileNotFoundException, ParameterException {
|
|
return terrainBrush(player, session, radius, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context);
|
|
}
|
|
|
|
private BrushSettings terrainBrush(Player player, LocalSession session, Expression radius, String image, int rotation, double yscale, boolean flat, boolean randomRotate, boolean layers, boolean smooth, ScalableHeightMap.Shape shape, CommandContext context) throws WorldEditException, FileNotFoundException, ParameterException {
|
|
checkMaxBrushRadius(radius);
|
|
InputStream stream = (image == null || image.equalsIgnoreCase("null") || image.equalsIgnoreCase("#clipboard")) ? null : getHeightmapStream(image);
|
|
HeightBrush brush;
|
|
if (flat) {
|
|
try {
|
|
brush = new FlattenBrush(stream, rotation, yscale, layers, smooth, "#clipboard".equalsIgnoreCase(image) ? session.getClipboard().getClipboard() : null, shape);
|
|
} catch (EmptyClipboardException ignore) {
|
|
brush = new FlattenBrush(stream, rotation, yscale, layers, smooth, null, shape);
|
|
}
|
|
} else {
|
|
try {
|
|
brush = new HeightBrush(stream, rotation, yscale, layers, smooth, "#clipboard".equalsIgnoreCase(image) ? session.getClipboard().getClipboard() : null);
|
|
} catch (EmptyClipboardException ignore) {
|
|
brush = new HeightBrush(stream, rotation, yscale, layers, smooth, null);
|
|
}
|
|
}
|
|
if (randomRotate) {
|
|
brush.setRandomRotate(true);
|
|
}
|
|
return set(session, context,
|
|
brush)
|
|
.setSize(radius);
|
|
}
|
|
|
|
private InputStream getHeightmapStream(String filename) throws FileNotFoundException, ParameterException {
|
|
if (filename == null) return null;
|
|
String filenamePng = (filename.endsWith(".png") ? filename : filename + ".png");
|
|
File file = new File(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HEIGHTMAP + File.separator + filenamePng);
|
|
if (file.exists()) return new FileInputStream(file);
|
|
URI uri = ImageUtil.getImageURI(filename);
|
|
return ImageUtil.getInputStream(uri);
|
|
}
|
|
|
|
|
|
@Command(
|
|
aliases = {"copypaste", "copy", "paste", "cp", "copypasta"},
|
|
usage = "[depth=5]",
|
|
desc = "Copy Paste brush",
|
|
help = "Left click the base of an object to copy.\n" +
|
|
"Right click to paste\n" +
|
|
"The -r flag Will apply random rotation on paste\n" +
|
|
"The -a flag Will apply auto view based rotation on paste\n" +
|
|
"Note: Works well with the clipboard scroll action\n" +
|
|
"Video: https://www.youtube.com/watch?v=RPZIaTbqoZw",
|
|
min = 0,
|
|
max = 1
|
|
)
|
|
@CommandPermissions("worldedit.brush.copy")
|
|
public BrushSettings copy(Player player, LocalSession session, @Optional("5") Expression radius, @Switch('r') boolean randomRotate, @Switch('a') boolean autoRotate, CommandContext context) throws WorldEditException {
|
|
checkMaxBrushRadius(radius);
|
|
player.print(BBC.getPrefix() + BBC.BRUSH_COPY.f(radius));
|
|
|
|
return set(session, context,
|
|
new CopyPastaBrush(player, session, randomRotate, autoRotate))
|
|
.setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"command", "cmd"},
|
|
usage = "<radius> [cmd1;cmd2...]",
|
|
desc = "Command brush",
|
|
help =
|
|
"Run the commands at the clicked position.\n" +
|
|
" - Your selection will be expanded to the specified size around each point\n" +
|
|
" - Placeholders: {x}, {y}, {z}, {world}, {size}",
|
|
|
|
min = 2,
|
|
max = 99
|
|
)
|
|
@CommandPermissions("worldedit.brush.command")
|
|
public BrushSettings command(Player player, LocalSession session, Expression radius, CommandContext args, CommandContext context) throws WorldEditException {
|
|
String cmd = args.getJoinedStrings(1);
|
|
return set(session, context,
|
|
new CommandBrush(cmd))
|
|
.setSize(radius);
|
|
}
|
|
|
|
@Command(
|
|
aliases = {"butcher", "kill"},
|
|
usage = "[radius=5]",
|
|
flags = "plangbtfr",
|
|
desc = "Butcher brush",
|
|
help = "Kills nearby mobs within the specified radius.\n" +
|
|
"Flags:\n" +
|
|
" -p also kills pets.\n" +
|
|
" -n also kills NPCs.\n" +
|
|
" -g also kills Golems.\n" +
|
|
" -a also kills animals.\n" +
|
|
" -b also kills ambient mobs.\n" +
|
|
" -t also kills mobs with name tags.\n" +
|
|
" -f compounds all previous flags.\n" +
|
|
" -r also destroys armor stands.\n" +
|
|
" -l currently does nothing.",
|
|
min = 0,
|
|
max = 1
|
|
)
|
|
@CommandPermissions("worldedit.brush.butcher")
|
|
public BrushSettings butcherBrush(Player player, LocalSession session, CommandContext args, CommandContext context) throws WorldEditException {
|
|
LocalConfiguration config = getWorldEdit().getConfiguration();
|
|
|
|
double radius = args.argsLength() > 0 ? args.getDouble(0) : 5;
|
|
double maxRadius = config.maxBrushRadius;
|
|
// hmmmm not horribly worried about this because -1 is still rather efficient,
|
|
// the problem arises when butcherMaxRadius is some really high number but not infinite
|
|
// - original idea taken from https://github.com/sk89q/worldedit/pull/198#issuecomment-6463108
|
|
if (player.hasPermission("worldedit.butcher")) {
|
|
maxRadius = Math.max(config.maxBrushRadius, config.butcherMaxRadius);
|
|
}
|
|
if (radius > maxRadius && maxRadius != -1) {
|
|
BBC.TOOL_RADIUS_ERROR.send(player, maxRadius);
|
|
return null;
|
|
}
|
|
|
|
CreatureButcher flags = new CreatureButcher(player);
|
|
flags.fromCommand(args);
|
|
|
|
return set(session, context,
|
|
new ButcherBrush(flags))
|
|
.setSize(radius);
|
|
}
|
|
|
|
public static Class<?> inject() {
|
|
return BrushCommands.class;
|
|
}
|
|
}
|