Change //stack and //move to take a full offset

This commit is contained in:
Octavia Togami 2020-07-09 20:52:46 -07:00
parent 043faaf55f
commit 5f9f10599e
9 changed files with 205 additions and 45 deletions

View File

@ -1289,7 +1289,7 @@ public int stackCuboidRegion(Region region, BlockVector3 dir, int count, boolean
* Stack a cuboid region.
*
* @param region the region to stack
* @param dir the direction to stack
* @param offset how far to move the contents each stack
* @param count the number of times to stack
* @param copyEntities true to copy entities
* @param copyBiomes true to copy biomes
@ -1297,17 +1297,17 @@ public int stackCuboidRegion(Region region, BlockVector3 dir, int count, boolean
* @return number of blocks affected
* @throws MaxChangedBlocksException thrown if too many blocks are changed
*/
public int stackCuboidRegion(Region region, BlockVector3 dir, int count,
public int stackCuboidRegion(Region region, BlockVector3 offset, int count,
boolean copyEntities, boolean copyBiomes, Mask mask) throws MaxChangedBlocksException {
checkNotNull(region);
checkNotNull(dir);
checkNotNull(offset);
checkArgument(count >= 1, "count >= 1 required");
BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1);
BlockVector3 to = region.getMinimumPoint();
ForwardExtentCopy copy = new ForwardExtentCopy(this, region, this, to);
copy.setRepetitions(count);
copy.setTransform(new AffineTransform().translate(dir.multiply(size)));
copy.setTransform(new AffineTransform().translate(offset.multiply(size)));
copy.setCopyingEntities(copyEntities);
copy.setCopyingBiomes(copyBiomes);
if (mask != null) {
@ -1321,23 +1321,23 @@ public int stackCuboidRegion(Region region, BlockVector3 dir, int count,
* Move the blocks in a region a certain direction.
*
* @param region the region to move
* @param dir the direction
* @param distance the distance to move
* @param offset the offset
* @param multiplier the number to multiply the offset by
* @param copyAir true to copy air blocks
* @param replacement the replacement pattern to fill in after moving, or null to use air
* @return number of blocks moved
* @throws MaxChangedBlocksException thrown if too many blocks are changed
*/
public int moveRegion(Region region, BlockVector3 dir, int distance, boolean copyAir, Pattern replacement) throws MaxChangedBlocksException {
return moveRegion(region, dir, distance, true, false, copyAir ? new ExistingBlockMask(this) : null, replacement);
public int moveRegion(Region region, BlockVector3 offset, int multiplier, boolean copyAir, Pattern replacement) throws MaxChangedBlocksException {
return moveRegion(region, offset, multiplier, true, false, copyAir ? new ExistingBlockMask(this) : null, replacement);
}
/**
* Move the blocks in a region a certain direction.
*
* @param region the region to move
* @param dir the direction
* @param distance the distance to move
* @param offset the offset
* @param multiplier the number to multiply the offset by
* @param moveEntities true to move entities
* @param copyBiomes true to copy biomes (source biome is unchanged)
* @param mask source mask for the operation (only matching blocks are moved)
@ -1346,11 +1346,11 @@ public int moveRegion(Region region, BlockVector3 dir, int distance, boolean cop
* @throws MaxChangedBlocksException thrown if too many blocks are changed
* @throws IllegalArgumentException thrown if the region is not a flat region, but copyBiomes is true
*/
public int moveRegion(Region region, BlockVector3 dir, int distance,
public int moveRegion(Region region, BlockVector3 offset, int multiplier,
boolean moveEntities, boolean copyBiomes, Mask mask, Pattern replacement) throws MaxChangedBlocksException {
checkNotNull(region);
checkNotNull(dir);
checkArgument(distance >= 1, "distance >= 1 required");
checkNotNull(offset);
checkArgument(multiplier >= 1, "multiplier >= 1 required");
checkArgument(!copyBiomes || region instanceof FlatRegion, "can't copy biomes from non-flat region");
BlockVector3 to = region.getMinimumPoint();
@ -1364,7 +1364,7 @@ public int moveRegion(Region region, BlockVector3 dir, int distance,
// Copy to a buffer so we don't destroy our original before we can copy all the blocks from it
ForgetfulExtentBuffer buffer = new ForgetfulExtentBuffer(this, new RegionMask(region));
ForwardExtentCopy copy = new ForwardExtentCopy(this, region, buffer, to);
copy.setTransform(new AffineTransform().translate(dir.multiply(distance)));
copy.setTransform(new AffineTransform().translate(offset.multiply(multiplier)));
copy.setSourceFunction(remove); // Remove
copy.setCopyingEntities(moveEntities);

View File

@ -41,7 +41,7 @@
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.visitor.LayerVisitor;
import com.sk89q.worldedit.function.visitor.RegionVisitor;
import com.sk89q.worldedit.internal.annotation.Direction;
import com.sk89q.worldedit.internal.annotation.Offset;
import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.internal.expression.ExpressionException;
import com.sk89q.worldedit.math.BlockVector3;
@ -299,11 +299,11 @@ public int smooth(Actor actor, EditSession editSession, @Selection Region region
@Logging(ORIENTATION_REGION)
public int move(Actor actor, World world, EditSession editSession, LocalSession session,
@Selection Region region,
@Arg(desc = "# of blocks to move", def = "1")
int count,
@Arg(desc = "The direction to move", def = Direction.AIM)
@Direction(includeDiagonals = true)
BlockVector3 direction,
@Arg(desc = "number of times to apply the offset", def = "1")
int multiplier,
@Arg(desc = "The offset to move", def = Offset.FORWARD)
@Offset
BlockVector3 offset,
@Arg(desc = "The pattern of blocks to leave", def = "air")
Pattern replace,
@Switch(name = 's', desc = "Shift the selection to the target location")
@ -316,7 +316,7 @@ public int move(Actor actor, World world, EditSession editSession, LocalSession
boolean copyBiomes,
@ArgFlag(name = 'm', desc = "Set the include mask, non-matching blocks become air")
Mask mask) throws WorldEditException {
checkCommandArgument(count >= 1, "Count must be >= 1");
checkCommandArgument(multiplier >= 1, "Multiplier must be >= 1");
Mask combinedMask;
if (ignoreAirBlocks) {
@ -329,11 +329,11 @@ public int move(Actor actor, World world, EditSession editSession, LocalSession
combinedMask = mask;
}
int affected = editSession.moveRegion(region, direction, count, copyEntities, copyBiomes, combinedMask, replace);
int affected = editSession.moveRegion(region, offset, multiplier, copyEntities, copyBiomes, combinedMask, replace);
if (moveSelection) {
try {
region.shift(direction.multiply(count));
region.shift(offset.multiply(multiplier));
session.getRegionSelector(world).learnChanges();
session.getRegionSelector(world).explainRegionAdjust(actor, session);
@ -356,9 +356,9 @@ public int stack(Actor actor, World world, EditSession editSession, LocalSession
@Selection Region region,
@Arg(desc = "# of copies to stack", def = "1")
int count,
@Arg(desc = "The direction to stack", def = Direction.AIM)
@Direction(includeDiagonals = true)
BlockVector3 direction,
@Arg(desc = "How far to move the contents each stack", def = Offset.FORWARD)
@Offset
BlockVector3 offset,
@Switch(name = 's', desc = "Shift the selection to the last stacked copy")
boolean moveSelection,
@Switch(name = 'a', desc = "Ignore air blocks")
@ -381,13 +381,13 @@ public int stack(Actor actor, World world, EditSession editSession, LocalSession
combinedMask = mask;
}
int affected = editSession.stackCuboidRegion(region, direction, count, copyEntities, copyBiomes, combinedMask);
int affected = editSession.stackCuboidRegion(region, offset, count, copyEntities, copyBiomes, combinedMask);
if (moveSelection) {
try {
final BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1);
final BlockVector3 shiftVector = direction.multiply(size).multiply(count);
final BlockVector3 shiftVector = offset.multiply(size).multiply(count);
region.shift(shiftVector);
session.getRegionSelector(world).learnChanges();

View File

@ -25,9 +25,9 @@
import com.sk89q.worldedit.UnknownDirectionException;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.internal.annotation.Direction;
import com.sk89q.worldedit.internal.annotation.MultiDirection;
import com.sk89q.worldedit.internal.annotation.OptionalArg;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import org.enginehub.piston.CommandManager;
@ -58,12 +58,12 @@ private static MultiDirection multiDirection(boolean includeDiagonals) {
protected static <D> void register(CommandManager commandManager, AbstractDirectionConverter<D> converter,
Class<D> keyClass, boolean includeDiagonals) {
commandManager.registerConverter(
Key.of(keyClass, direction(includeDiagonals)),
converter
Key.of(keyClass, direction(includeDiagonals)),
converter
);
commandManager.registerConverter(
Key.of(keyClass, multiDirection(includeDiagonals)),
CommaSeparatedValuesConverter.wrap(converter)
Key.of(keyClass, multiDirection(includeDiagonals)),
CommaSeparatedValuesConverter.wrap(converter)
);
}
@ -93,8 +93,8 @@ protected AbstractDirectionConverter(WorldEdit worldEdit, boolean includeDiagona
@Override
public ConversionResult<D> convert(String argument, InjectedValueAccess context) {
Player player = context.injectedValue(Key.of(Actor.class))
.filter(Player.class::isInstance).map(Player.class::cast).orElse(null);
Player player = context.injectedValue(Key.of(Player.class, OptionalArg.class))
.orElse(null);
try {
return SuccessfulConversion.fromSingle(convertDirection(argument, player, includeDiagonals));
} catch (Exception e) {

View File

@ -29,7 +29,7 @@
public final class DirectionVectorConverter extends AbstractDirectionConverter<BlockVector3> {
private DirectionVectorConverter(WorldEdit worldEdit, boolean includeDiagonals) {
public DirectionVectorConverter(WorldEdit worldEdit, boolean includeDiagonals) {
super(worldEdit, includeDiagonals);
}

View File

@ -0,0 +1,76 @@
/*
* 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.argument;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.internal.annotation.Offset;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import org.enginehub.piston.CommandManager;
import org.enginehub.piston.converter.ArgumentConverter;
import org.enginehub.piston.converter.ConversionResult;
import org.enginehub.piston.inject.InjectedValueAccess;
import org.enginehub.piston.inject.Key;
import java.util.List;
public class OffsetConverter implements ArgumentConverter<BlockVector3> {
public static void register(WorldEdit worldEdit, CommandManager commandManager) {
commandManager.registerConverter(
Key.of(BlockVector3.class, Offset.class),
new OffsetConverter(worldEdit)
);
}
private final DirectionVectorConverter directionVectorConverter;
private final VectorConverter<Integer, BlockVector3> vectorConverter =
VectorConverter.BLOCK_VECTOR_3_CONVERTER;
private OffsetConverter(WorldEdit worldEdit) {
directionVectorConverter = new DirectionVectorConverter(worldEdit, true);
}
@Override
public Component describeAcceptableArguments() {
return TextComponent.builder()
.append(directionVectorConverter.describeAcceptableArguments())
.append(", or ")
.append(vectorConverter.describeAcceptableArguments())
.build();
}
@Override
public List<String> getSuggestions(String input, InjectedValueAccess context) {
return ImmutableList.copyOf(Iterables.concat(
directionVectorConverter.getSuggestions(input, context),
vectorConverter.getSuggestions(input, context)
));
}
@Override
public ConversionResult<BlockVector3> convert(String input, InjectedValueAccess context) {
return directionVectorConverter.convert(input, context)
.orElse(vectorConverter.convert(input, context));
}
}

View File

@ -40,12 +40,22 @@
import java.util.function.Function;
public class VectorConverter<C, T> implements ArgumentConverter<T> {
private static final CommaSeparatedValuesConverter<Integer> INT_CONVERTER =
CommaSeparatedValuesConverter.wrap(ArgumentConverters.get(TypeToken.of(int.class)));
public static final VectorConverter<Integer, BlockVector3> BLOCK_VECTOR_3_CONVERTER = new VectorConverter<>(
INT_CONVERTER,
3,
cmps -> BlockVector3.at(cmps.get(0), cmps.get(1), cmps.get(2)),
"block vector with x, y, and z"
);
public static void register(CommandManager commandManager) {
CommaSeparatedValuesConverter<Integer> intConverter = CommaSeparatedValuesConverter.wrap(ArgumentConverters.get(TypeToken.of(int.class)));
CommaSeparatedValuesConverter<Double> doubleConverter = CommaSeparatedValuesConverter.wrap(ArgumentConverters.get(TypeToken.of(double.class)));
commandManager.registerConverter(Key.of(BlockVector2.class),
new VectorConverter<>(
intConverter,
INT_CONVERTER,
2,
cmps -> BlockVector2.at(cmps.get(0), cmps.get(1)),
"block vector with x and z"
@ -57,13 +67,7 @@ public static void register(CommandManager commandManager) {
cmps -> Vector2.at(cmps.get(0), cmps.get(1)),
"vector with x and z"
));
commandManager.registerConverter(Key.of(BlockVector3.class),
new VectorConverter<>(
intConverter,
3,
cmps -> BlockVector3.at(cmps.get(0), cmps.get(1), cmps.get(2)),
"block vector with x, y, and z"
));
commandManager.registerConverter(Key.of(BlockVector3.class), BLOCK_VECTOR_3_CONVERTER);
commandManager.registerConverter(Key.of(Vector3.class),
new VectorConverter<>(
doubleConverter,

View File

@ -77,6 +77,7 @@
import com.sk89q.worldedit.command.argument.EnumConverter;
import com.sk89q.worldedit.command.argument.FactoryConverter;
import com.sk89q.worldedit.command.argument.HeightConverter;
import com.sk89q.worldedit.command.argument.OffsetConverter;
import com.sk89q.worldedit.command.argument.RegionFactoryConverter;
import com.sk89q.worldedit.command.argument.RegistryConverter;
import com.sk89q.worldedit.command.argument.SideEffectConverter;
@ -90,6 +91,7 @@
import com.sk89q.worldedit.event.platform.CommandEvent;
import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.annotation.OptionalArg;
import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.internal.command.CommandArgParser;
import com.sk89q.worldedit.internal.command.CommandLoggingHandler;
@ -220,6 +222,7 @@ private void registerArgumentConverters() {
WorldConverter.register(commandManager);
SideEffectConverter.register(commandManager);
HeightConverter.register(commandManager);
OffsetConverter.register(worldEdit, commandManager);
}
private void registerAlwaysInjectedValues() {
@ -559,6 +562,7 @@ private MemoizingValueAccess initializeInjectedValues(Arguments arguments, Actor
store.injectValue(Key.of(Actor.class), ValueProvider.constant(actor));
if (actor instanceof Player) {
store.injectValue(Key.of(Player.class), ValueProvider.constant((Player) actor));
store.injectValue(Key.of(Player.class, OptionalArg.class), ValueProvider.constant((Player) actor));
} else {
store.injectValue(Key.of(Player.class), context -> {
throw new CommandException(TranslatableComponent.of("worldedit.command.player-only"), ImmutableList.of());

View File

@ -0,0 +1,38 @@
/*
* 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.internal.annotation;
import com.sk89q.worldedit.math.BlockVector3;
import org.enginehub.piston.inject.InjectAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates a {@link BlockVector3} parameter to inject an offset.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@InjectAnnotation
public @interface Offset {
String FORWARD = "forward";
}

View File

@ -0,0 +1,38 @@
/*
* 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.internal.annotation;
import com.sk89q.worldedit.entity.Player;
import org.enginehub.piston.inject.InjectAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates a parameter to indicate it as optional. This is really a bit of a hack, used to
* get a {@link Player} or {@code null} instead of throwing.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@InjectAnnotation
public @interface OptionalArg {
}