2010-10-02 21:52:42 +00:00
|
|
|
/*
|
2014-03-31 23:55:58 +00:00
|
|
|
* WorldEdit, a Minecraft world manipulation toolkit
|
|
|
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
|
|
|
* Copyright (C) WorldEdit team and contributors
|
2010-10-02 21:52:42 +00:00
|
|
|
*
|
2014-04-04 22:03:18 +00:00
|
|
|
* 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
|
2010-10-02 21:52:42 +00:00
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
2014-04-04 22:03:18 +00:00
|
|
|
* 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.
|
2010-10-02 21:52:42 +00:00
|
|
|
*
|
2014-04-04 22:03:18 +00:00
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
2010-10-02 21:52:42 +00:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2011-01-01 18:33:18 +00:00
|
|
|
*/
|
2014-03-31 23:55:58 +00:00
|
|
|
|
2011-01-01 01:06:42 +00:00
|
|
|
package com.sk89q.worldedit;
|
|
|
|
|
2020-03-21 01:39:40 +00:00
|
|
|
import static com.google.common.base.Preconditions.checkArgument;
|
|
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
|
|
import static com.sk89q.worldedit.regions.Regions.asFlatRegion;
|
|
|
|
import static com.sk89q.worldedit.regions.Regions.maximumBlockY;
|
|
|
|
import static com.sk89q.worldedit.regions.Regions.minimumBlockY;
|
|
|
|
|
2019-10-23 14:35:04 +00:00
|
|
|
import com.boydti.fawe.FaweCache;
|
2019-12-16 09:01:41 +00:00
|
|
|
import com.boydti.fawe.config.Caption;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.boydti.fawe.config.Settings;
|
2019-04-03 05:53:34 +00:00
|
|
|
import com.boydti.fawe.object.FaweLimit;
|
|
|
|
import com.boydti.fawe.object.RegionWrapper;
|
|
|
|
import com.boydti.fawe.object.RunnableVal;
|
2020-01-06 08:36:16 +00:00
|
|
|
import com.boydti.fawe.object.changeset.AbstractChangeSet;
|
2020-03-21 01:39:40 +00:00
|
|
|
import com.boydti.fawe.object.changeset.BlockBagChangeSet;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.boydti.fawe.object.collection.LocalBlockVectorSet;
|
2019-07-18 06:56:57 +00:00
|
|
|
import com.boydti.fawe.object.extent.FaweRegionExtent;
|
|
|
|
import com.boydti.fawe.object.extent.ProcessedWEExtent;
|
|
|
|
import com.boydti.fawe.object.extent.ResettableExtent;
|
|
|
|
import com.boydti.fawe.object.extent.SourceMaskExtent;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.boydti.fawe.object.function.SurfaceRegionFunction;
|
|
|
|
import com.boydti.fawe.object.mask.ResettableMask;
|
|
|
|
import com.boydti.fawe.object.pattern.ExistingPattern;
|
2019-07-18 06:56:57 +00:00
|
|
|
import com.boydti.fawe.util.EditSessionBuilder;
|
2019-04-03 05:53:34 +00:00
|
|
|
import com.boydti.fawe.util.ExtentTraverser;
|
|
|
|
import com.boydti.fawe.util.MaskTraverser;
|
|
|
|
import com.boydti.fawe.util.MathMan;
|
|
|
|
import com.boydti.fawe.util.TaskManager;
|
2019-09-14 03:05:16 +00:00
|
|
|
import com.sk89q.worldedit.entity.Player;
|
2014-04-03 01:57:25 +00:00
|
|
|
import com.sk89q.worldedit.event.extent.EditSessionEvent;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
|
2014-04-23 07:33:00 +00:00
|
|
|
import com.sk89q.worldedit.extent.ChangeSetExtent;
|
|
|
|
import com.sk89q.worldedit.extent.Extent;
|
|
|
|
import com.sk89q.worldedit.extent.MaskingExtent;
|
2019-08-06 20:38:25 +00:00
|
|
|
import com.sk89q.worldedit.extent.PassthroughExtent;
|
2014-04-23 07:33:00 +00:00
|
|
|
import com.sk89q.worldedit.extent.inventory.BlockBag;
|
2014-04-03 02:08:50 +00:00
|
|
|
import com.sk89q.worldedit.extent.inventory.BlockBagExtent;
|
2014-04-23 07:33:00 +00:00
|
|
|
import com.sk89q.worldedit.extent.world.SurvivalModeExtent;
|
2019-09-05 05:50:14 +00:00
|
|
|
import com.sk89q.worldedit.extent.world.WatchdogTickingExtent;
|
2014-03-28 23:49:01 +00:00
|
|
|
import com.sk89q.worldedit.function.GroundFunction;
|
2018-08-12 14:03:07 +00:00
|
|
|
import com.sk89q.worldedit.function.RegionFunction;
|
2014-03-28 23:15:40 +00:00
|
|
|
import com.sk89q.worldedit.function.block.BlockReplace;
|
2014-03-30 01:32:10 +00:00
|
|
|
import com.sk89q.worldedit.function.block.Naturalizer;
|
2019-03-21 01:05:11 +00:00
|
|
|
import com.sk89q.worldedit.function.generator.ForestGenerator;
|
2014-03-28 23:15:40 +00:00
|
|
|
import com.sk89q.worldedit.function.generator.GardenPatchGenerator;
|
2019-07-06 00:46:48 +00:00
|
|
|
import com.sk89q.worldedit.function.mask.BlockTypeMask;
|
|
|
|
import com.sk89q.worldedit.function.mask.BoundedHeightMask;
|
|
|
|
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
|
|
|
|
import com.sk89q.worldedit.function.mask.Mask;
|
|
|
|
import com.sk89q.worldedit.function.mask.MaskIntersection;
|
|
|
|
import com.sk89q.worldedit.function.mask.MaskUnion;
|
|
|
|
import com.sk89q.worldedit.function.mask.Masks;
|
|
|
|
import com.sk89q.worldedit.function.mask.NoiseFilter2D;
|
|
|
|
import com.sk89q.worldedit.function.mask.RegionMask;
|
2019-07-18 06:56:57 +00:00
|
|
|
import com.sk89q.worldedit.function.mask.SingleBlockTypeMask;
|
|
|
|
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
2018-06-19 07:03:09 +00:00
|
|
|
import com.sk89q.worldedit.function.operation.ChangeSetExecutor;
|
|
|
|
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
|
|
|
|
import com.sk89q.worldedit.function.operation.Operations;
|
2018-06-15 05:41:37 +00:00
|
|
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
2019-06-12 21:12:12 +00:00
|
|
|
import com.sk89q.worldedit.function.pattern.WaterloggedRemover;
|
2014-03-28 23:49:01 +00:00
|
|
|
import com.sk89q.worldedit.function.util.RegionOffset;
|
2019-04-03 05:53:34 +00:00
|
|
|
import com.sk89q.worldedit.function.visitor.DirectionalVisitor;
|
2018-06-19 07:03:09 +00:00
|
|
|
import com.sk89q.worldedit.function.visitor.DownwardVisitor;
|
2019-04-03 05:53:34 +00:00
|
|
|
import com.sk89q.worldedit.function.visitor.FlatRegionVisitor;
|
2018-06-19 07:03:09 +00:00
|
|
|
import com.sk89q.worldedit.function.visitor.LayerVisitor;
|
|
|
|
import com.sk89q.worldedit.function.visitor.NonRisingVisitor;
|
|
|
|
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
|
|
|
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
2014-03-31 05:33:16 +00:00
|
|
|
import com.sk89q.worldedit.history.UndoContext;
|
2014-03-31 23:55:58 +00:00
|
|
|
import com.sk89q.worldedit.history.changeset.ChangeSet;
|
2019-11-19 22:07:30 +00:00
|
|
|
import com.sk89q.worldedit.internal.expression.EvaluationException;
|
2014-04-23 07:33:00 +00:00
|
|
|
import com.sk89q.worldedit.internal.expression.Expression;
|
|
|
|
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
2020-01-03 20:38:30 +00:00
|
|
|
import com.sk89q.worldedit.internal.expression.ExpressionTimeoutException;
|
|
|
|
import com.sk89q.worldedit.internal.expression.LocalSlot.Variable;
|
2018-10-14 10:40:53 +00:00
|
|
|
import com.sk89q.worldedit.math.BlockVector2;
|
|
|
|
import com.sk89q.worldedit.math.BlockVector3;
|
2019-08-06 15:29:09 +00:00
|
|
|
import com.sk89q.worldedit.math.MathUtils;
|
2019-04-03 05:53:34 +00:00
|
|
|
import com.sk89q.worldedit.math.MutableBlockVector2;
|
|
|
|
import com.sk89q.worldedit.math.MutableBlockVector3;
|
2018-10-14 10:40:53 +00:00
|
|
|
import com.sk89q.worldedit.math.Vector2;
|
|
|
|
import com.sk89q.worldedit.math.Vector3;
|
2014-03-30 19:13:45 +00:00
|
|
|
import com.sk89q.worldedit.math.interpolation.Interpolation;
|
|
|
|
import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
|
|
|
|
import com.sk89q.worldedit.math.interpolation.Node;
|
2014-03-30 08:36:02 +00:00
|
|
|
import com.sk89q.worldedit.math.noise.RandomNoise;
|
2014-03-30 10:08:56 +00:00
|
|
|
import com.sk89q.worldedit.math.transform.AffineTransform;
|
2018-06-19 07:03:09 +00:00
|
|
|
import com.sk89q.worldedit.regions.CuboidRegion;
|
|
|
|
import com.sk89q.worldedit.regions.EllipsoidRegion;
|
|
|
|
import com.sk89q.worldedit.regions.FlatRegion;
|
|
|
|
import com.sk89q.worldedit.regions.Region;
|
|
|
|
import com.sk89q.worldedit.regions.Regions;
|
2014-04-03 02:08:50 +00:00
|
|
|
import com.sk89q.worldedit.regions.shape.ArbitraryBiomeShape;
|
|
|
|
import com.sk89q.worldedit.regions.shape.ArbitraryShape;
|
|
|
|
import com.sk89q.worldedit.regions.shape.RegionShape;
|
|
|
|
import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
|
2018-06-19 07:03:09 +00:00
|
|
|
import com.sk89q.worldedit.util.Countable;
|
2018-12-23 16:19:33 +00:00
|
|
|
import com.sk89q.worldedit.util.Direction;
|
2018-06-19 07:03:09 +00:00
|
|
|
import com.sk89q.worldedit.util.TreeGenerator;
|
2014-04-03 01:57:25 +00:00
|
|
|
import com.sk89q.worldedit.util.eventbus.EventBus;
|
2020-01-03 20:38:30 +00:00
|
|
|
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
2014-04-05 09:59:38 +00:00
|
|
|
import com.sk89q.worldedit.world.World;
|
2019-02-16 07:27:00 +00:00
|
|
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
2019-06-25 13:00:44 +00:00
|
|
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
|
|
|
import com.sk89q.worldedit.world.block.BlockCategories;
|
|
|
|
import com.sk89q.worldedit.world.block.BlockID;
|
|
|
|
import com.sk89q.worldedit.world.block.BlockState;
|
|
|
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
|
|
|
import com.sk89q.worldedit.world.block.BlockType;
|
|
|
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
2019-11-20 00:11:54 +00:00
|
|
|
import com.sk89q.worldedit.world.registry.LegacyMapper;
|
2020-02-06 00:43:50 +00:00
|
|
|
import java.io.IOException;
|
2019-06-25 13:00:44 +00:00
|
|
|
import java.util.ArrayList;
|
2019-07-18 06:56:57 +00:00
|
|
|
import java.util.Collection;
|
2019-06-25 13:00:44 +00:00
|
|
|
import java.util.Collections;
|
2019-07-18 06:56:57 +00:00
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.Iterator;
|
2019-06-25 13:00:44 +00:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
2020-03-27 02:26:11 +00:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
2020-03-21 01:39:40 +00:00
|
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
2018-08-12 14:03:07 +00:00
|
|
|
|
2010-10-02 21:52:42 +00:00
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* An {@link Extent} that handles history, {@link BlockBag}s, change limits,
|
|
|
|
* block re-ordering, and much more. Most operations in WorldEdit use this class.
|
2019-04-02 22:21:02 +00:00
|
|
|
*
|
2014-07-29 14:57:45 +00:00
|
|
|
* <p>Most of the actual functionality is implemented with a number of other
|
2014-03-31 23:55:58 +00:00
|
|
|
* {@link Extent}s that are chained together. For example, history is logged
|
2014-07-29 14:57:45 +00:00
|
|
|
* using the {@link ChangeSetExtent}.</p>
|
2010-10-02 21:52:42 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
@SuppressWarnings({"FieldCanBeLocal"})
|
2019-08-18 01:09:09 +00:00
|
|
|
public class EditSession extends PassthroughExtent implements AutoCloseable {
|
2011-08-07 00:40:48 +00:00
|
|
|
|
2019-03-14 02:51:48 +00:00
|
|
|
private static final Logger log = LoggerFactory.getLogger(EditSession.class);
|
2011-09-24 19:32:03 +00:00
|
|
|
|
2010-10-05 07:40:50 +00:00
|
|
|
/**
|
2018-12-23 16:19:33 +00:00
|
|
|
* Used by {@link EditSession#setBlock(BlockVector3, BlockStateHolder, Stage)} to
|
2014-03-31 23:55:58 +00:00
|
|
|
* determine which {@link Extent}s should be bypassed.
|
2010-10-05 07:40:50 +00:00
|
|
|
*/
|
2019-04-03 11:28:57 +00:00
|
|
|
public enum Stage {
|
|
|
|
BEFORE_HISTORY,
|
|
|
|
BEFORE_REORDER,
|
|
|
|
BEFORE_CHANGE
|
|
|
|
}
|
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
/**
|
|
|
|
* Reorder mode for {@link EditSession#setReorderMode(ReorderMode)}.
|
|
|
|
* NOT FUNCTIONAL IN FAWE AS OF June 3,2019
|
|
|
|
*
|
|
|
|
* MULTI_STAGE = Multi stage reorder, may not be great with mods.
|
|
|
|
* FAST = Use the fast mode. Good for mods.
|
|
|
|
* NONE = Place blocks without worrying about placement order.
|
|
|
|
*/
|
|
|
|
public enum ReorderMode {
|
|
|
|
MULTI_STAGE("multi"),
|
|
|
|
FAST("fast"),
|
|
|
|
NONE("none");
|
|
|
|
|
2020-04-23 16:07:01 +00:00
|
|
|
private final String displayName;
|
2019-06-04 15:48:30 +00:00
|
|
|
|
|
|
|
ReorderMode(String displayName) {
|
|
|
|
this.displayName = displayName;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getDisplayName() {
|
|
|
|
return this.displayName;
|
|
|
|
}
|
|
|
|
}
|
2019-11-21 07:57:32 +00:00
|
|
|
|
2020-01-03 20:38:30 +00:00
|
|
|
@SuppressWarnings("ProtectedField")
|
|
|
|
protected final World world;
|
2019-07-18 06:56:57 +00:00
|
|
|
private final FaweLimit originalLimit;
|
|
|
|
private final FaweLimit limit;
|
2019-09-14 03:05:16 +00:00
|
|
|
private final Player player;
|
2020-01-06 08:36:16 +00:00
|
|
|
private AbstractChangeSet changeSet;
|
2019-10-23 04:23:52 +00:00
|
|
|
private boolean history;
|
2018-08-12 14:03:07 +00:00
|
|
|
|
2019-07-18 06:56:57 +00:00
|
|
|
private final MutableBlockVector3 mutablebv = new MutableBlockVector3();
|
2018-08-12 14:03:07 +00:00
|
|
|
|
2020-05-13 21:45:11 +00:00
|
|
|
private int changes = 0;
|
2019-07-18 06:56:57 +00:00
|
|
|
private final BlockBag blockBag;
|
2011-09-24 19:32:03 +00:00
|
|
|
|
2020-04-23 16:07:01 +00:00
|
|
|
private final Extent bypassHistory;
|
2020-01-03 20:38:30 +00:00
|
|
|
private Extent bypassAll;
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
private final int maxY;
|
2020-03-04 02:41:51 +00:00
|
|
|
private final List<WatchdogTickingExtent> watchdogExtents = new ArrayList<>(2);
|
|
|
|
|
2011-09-24 19:32:03 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
@Deprecated
|
2020-03-27 02:26:11 +00:00
|
|
|
public EditSession(@NotNull EventBus bus, World world, @Nullable Player player,
|
2020-02-12 21:18:36 +00:00
|
|
|
@Nullable FaweLimit limit, @Nullable AbstractChangeSet changeSet,
|
|
|
|
@Nullable RegionWrapper[] allowedRegions, @Nullable Boolean autoQueue,
|
|
|
|
@Nullable Boolean fastmode, @Nullable Boolean checkMemory, @Nullable Boolean combineStages,
|
|
|
|
@Nullable BlockBag blockBag, @Nullable EditSessionEvent event) {
|
2020-03-27 02:26:11 +00:00
|
|
|
this(new EditSessionBuilder(world).player(player).limit(limit).changeSet(changeSet).allowedRegions(allowedRegions).autoQueue(autoQueue).fastmode(fastmode).checkMemory(checkMemory).combineStages(combineStages).blockBag(blockBag).eventBus(bus).event(event));
|
2020-02-12 21:18:36 +00:00
|
|
|
}
|
2020-03-27 02:26:11 +00:00
|
|
|
|
2020-02-12 21:18:36 +00:00
|
|
|
/**
|
|
|
|
* Construct the object with a maximum number of blocks and a block bag.
|
|
|
|
*
|
|
|
|
* @param eventBus the event bus
|
|
|
|
* @param world the world
|
|
|
|
* @param maxBlocks the maximum number of blocks that can be changed, or -1 to use no limit
|
|
|
|
* @param blockBag an optional {@link BlockBag} to use, otherwise null
|
|
|
|
* @param event the event to call with the extent
|
|
|
|
*/
|
2020-03-27 02:26:11 +00:00
|
|
|
public EditSession(@NotNull EventBus eventBus, World world, int maxBlocks, @Nullable BlockBag blockBag, EditSessionEvent event) {
|
2020-02-12 21:18:36 +00:00
|
|
|
this(eventBus, world, null, null, null, null, true, null, null, null, blockBag, event);
|
2019-07-18 06:56:57 +00:00
|
|
|
}
|
2019-06-25 13:00:44 +00:00
|
|
|
|
2019-07-18 06:56:57 +00:00
|
|
|
public EditSession(EditSessionBuilder builder) {
|
|
|
|
super(builder.compile().getExtent());
|
|
|
|
this.world = builder.getWorld();
|
|
|
|
this.bypassHistory = builder.getBypassHistory();
|
|
|
|
this.bypassAll = builder.getBypassAll();
|
|
|
|
this.originalLimit = builder.getLimit();
|
|
|
|
this.limit = builder.getLimit().copy();
|
|
|
|
this.player = builder.getPlayer();
|
2020-01-03 20:38:30 +00:00
|
|
|
this.changeSet = builder.getChangeTask();
|
2020-02-16 20:14:34 +00:00
|
|
|
this.maxY = world.getMaxY();
|
2019-07-18 06:56:57 +00:00
|
|
|
this.blockBag = builder.getBlockBag();
|
2020-01-03 20:38:30 +00:00
|
|
|
this.history = changeSet != null;
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The limit for this specific edit (blocks etc)
|
|
|
|
*
|
2020-04-23 16:07:01 +00:00
|
|
|
* @return The limit
|
2018-08-12 14:03:07 +00:00
|
|
|
*/
|
|
|
|
public FaweLimit getLimit() {
|
|
|
|
return originalLimit;
|
|
|
|
}
|
2010-12-31 22:31:49 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
public void resetLimit() {
|
|
|
|
this.limit.set(this.originalLimit);
|
2019-07-18 06:56:57 +00:00
|
|
|
ExtentTraverser<ProcessedWEExtent> find = new ExtentTraverser<>(getExtent()).find(ProcessedWEExtent.class);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (find != null && find.get() != null) {
|
|
|
|
find.get().setLimit(this.limit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a new limit representing how much of this edit's limit has been used so far
|
|
|
|
*
|
2020-04-23 16:07:01 +00:00
|
|
|
* @return Limit remaining
|
2018-08-12 14:03:07 +00:00
|
|
|
*/
|
|
|
|
public FaweLimit getLimitUsed() {
|
|
|
|
FaweLimit newLimit = new FaweLimit();
|
|
|
|
newLimit.MAX_ACTIONS = originalLimit.MAX_ACTIONS - limit.MAX_ACTIONS;
|
|
|
|
newLimit.MAX_CHANGES = originalLimit.MAX_CHANGES - limit.MAX_CHANGES;
|
|
|
|
newLimit.MAX_FAILS = originalLimit.MAX_FAILS - limit.MAX_FAILS;
|
|
|
|
newLimit.MAX_CHECKS = originalLimit.MAX_CHECKS - limit.MAX_CHECKS;
|
|
|
|
newLimit.MAX_ITERATIONS = originalLimit.MAX_ITERATIONS - limit.MAX_ITERATIONS;
|
|
|
|
newLimit.MAX_BLOCKSTATES = originalLimit.MAX_BLOCKSTATES - limit.MAX_BLOCKSTATES;
|
|
|
|
newLimit.MAX_ENTITIES = originalLimit.MAX_ENTITIES - limit.MAX_ENTITIES;
|
|
|
|
newLimit.MAX_HISTORY = limit.MAX_HISTORY;
|
|
|
|
return newLimit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the remaining limits
|
|
|
|
*
|
2020-04-23 16:07:01 +00:00
|
|
|
* @return remaining limits
|
2018-08-12 14:03:07 +00:00
|
|
|
*/
|
|
|
|
public FaweLimit getLimitLeft() {
|
|
|
|
return limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-04-03 05:53:34 +00:00
|
|
|
* The region extent restricts block placements to allowmaxYed regions
|
2018-08-12 14:03:07 +00:00
|
|
|
*
|
|
|
|
* @return FaweRegionExtent (may be null)
|
|
|
|
*/
|
|
|
|
public FaweRegionExtent getRegionExtent() {
|
2019-07-18 06:56:57 +00:00
|
|
|
ExtentTraverser<FaweRegionExtent> traverser = new ExtentTraverser<>(getExtent()).find(FaweRegionExtent.class);
|
2018-08-12 14:03:07 +00:00
|
|
|
return traverser == null ? null : traverser.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public Extent getBypassAll() {
|
|
|
|
return bypassAll;
|
|
|
|
}
|
|
|
|
public Extent getBypassHistory() {
|
|
|
|
return bypassHistory;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setExtent(AbstractDelegateExtent extent) {
|
2019-12-16 09:07:51 +00:00
|
|
|
new ExtentTraverser(this).setNext(extent);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-10-23 18:29:37 +00:00
|
|
|
* Get the Player or null
|
2018-08-12 14:03:07 +00:00
|
|
|
*
|
2019-10-23 18:29:37 +00:00
|
|
|
* @return the player
|
2018-08-12 14:03:07 +00:00
|
|
|
*/
|
|
|
|
@Nullable
|
2019-09-14 03:05:16 +00:00
|
|
|
public Player getPlayer() {
|
2018-08-12 14:03:07 +00:00
|
|
|
return player;
|
|
|
|
}
|
|
|
|
|
2018-11-04 05:22:43 +00:00
|
|
|
// pkg private for TracedEditSession only, may later become public API
|
|
|
|
boolean commitRequired() {
|
|
|
|
return false;
|
2018-10-31 01:39:51 +00:00
|
|
|
}
|
|
|
|
|
2018-10-04 17:37:58 +00:00
|
|
|
/**
|
|
|
|
* Turns on specific features for a normal WorldEdit session, such as
|
2018-12-09 05:11:11 +00:00
|
|
|
* {@link #setBatchingChunks(boolean)
|
2018-10-04 17:37:58 +00:00
|
|
|
* chunk batching}.
|
|
|
|
*/
|
|
|
|
public void enableStandardMode() {
|
2020-02-06 21:33:31 +00:00
|
|
|
setBatchingChunks(true);
|
2018-10-04 17:37:58 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
/**
|
|
|
|
* Sets the {@link ReorderMode} of this EditSession, and flushes the session.
|
|
|
|
*
|
|
|
|
* @param reorderMode The reorder mode
|
|
|
|
*/
|
|
|
|
public void setReorderMode(ReorderMode reorderMode) {
|
2019-07-18 16:07:31 +00:00
|
|
|
switch (reorderMode) {
|
|
|
|
case MULTI_STAGE:
|
|
|
|
enableQueue();
|
|
|
|
break;
|
|
|
|
case NONE: // Functionally the same, since FAWE doesn't perform physics
|
|
|
|
case FAST:
|
|
|
|
disableQueue();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new UnsupportedOperationException("Not implemented: " + reorderMode);
|
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the reorder mode.
|
|
|
|
*
|
|
|
|
* @return the reorder mode
|
|
|
|
*/
|
|
|
|
public ReorderMode getReorderMode() {
|
2019-07-18 16:07:31 +00:00
|
|
|
if (isQueueEnabled()) {
|
|
|
|
return ReorderMode.MULTI_STAGE;
|
|
|
|
}
|
|
|
|
return ReorderMode.FAST;
|
2019-06-04 15:48:30 +00:00
|
|
|
}
|
|
|
|
|
2010-10-11 08:22:47 +00:00
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Get the world.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @return the world
|
2010-10-11 08:22:47 +00:00
|
|
|
*/
|
2014-04-05 09:59:38 +00:00
|
|
|
public World getWorld() {
|
2019-03-28 19:02:37 +00:00
|
|
|
return world;
|
2010-10-05 07:40:50 +00:00
|
|
|
}
|
|
|
|
|
2011-03-13 00:37:07 +00:00
|
|
|
/**
|
2014-03-31 05:33:16 +00:00
|
|
|
* Get the underlying {@link ChangeSet}.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 05:33:16 +00:00
|
|
|
* @return the change set
|
2011-03-13 00:37:07 +00:00
|
|
|
*/
|
2014-03-31 05:33:16 +00:00
|
|
|
public ChangeSet getChangeSet() {
|
2020-01-03 20:38:30 +00:00
|
|
|
return changeSet;
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the ChangeSet without hooking into any recording mechanism or triggering any actions.<br/>
|
|
|
|
* Used internally to set the ChangeSet during completion to record custom changes which aren't normally recorded
|
2020-04-23 16:07:01 +00:00
|
|
|
* @param set The ChangeSet to set
|
2018-08-12 14:03:07 +00:00
|
|
|
*/
|
2020-01-06 08:36:16 +00:00
|
|
|
public void setRawChangeSet(@Nullable AbstractChangeSet set) {
|
2020-01-03 20:38:30 +00:00
|
|
|
changeSet = set;
|
2018-08-12 14:03:07 +00:00
|
|
|
changes++;
|
2014-03-31 05:33:16 +00:00
|
|
|
}
|
2011-03-13 00:37:07 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
/**
|
2019-06-04 15:48:30 +00:00
|
|
|
* Get the maximum number of blocks that can be changed. -1 will be returned
|
|
|
|
* if it the limit disabled.
|
|
|
|
*
|
2014-07-29 21:23:00 +00:00
|
|
|
* @return the limit (>= 0) or -1 for no limit
|
2018-08-12 14:03:07 +00:00
|
|
|
* @see #getLimit()
|
2011-02-18 23:14:43 +00:00
|
|
|
*/
|
2018-08-12 14:03:07 +00:00
|
|
|
@Deprecated
|
2014-03-31 23:55:58 +00:00
|
|
|
public int getBlockChangeLimit() {
|
2018-08-12 14:03:07 +00:00
|
|
|
return originalLimit.MAX_CHANGES;
|
2011-02-18 23:14:43 +00:00
|
|
|
}
|
|
|
|
|
2010-10-13 05:38:05 +00:00
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Set the maximum number of blocks that can be changed.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-07-29 21:23:00 +00:00
|
|
|
* @param limit the limit (>= 0) or -1 for no limit
|
2010-10-13 05:38:05 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public void setBlockChangeLimit(int limit) {
|
2019-11-21 07:57:32 +00:00
|
|
|
this.limit.MAX_CHANGES = limit;
|
2010-10-13 05:38:05 +00:00
|
|
|
}
|
|
|
|
|
2010-10-05 07:40:50 +00:00
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Returns queue status.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @return whether the queue is enabled
|
2019-06-12 21:12:12 +00:00
|
|
|
* @deprecated Use {@link EditSession#getReorderMode()} with MULTI_STAGE instead.
|
2010-10-05 07:40:50 +00:00
|
|
|
*/
|
2019-03-28 19:02:37 +00:00
|
|
|
@Deprecated
|
2014-03-31 23:55:58 +00:00
|
|
|
public boolean isQueueEnabled() {
|
2018-08-12 14:03:07 +00:00
|
|
|
return true;
|
2010-10-02 21:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Queue certain types of block for better reproduction of those blocks.
|
2019-06-12 21:12:12 +00:00
|
|
|
*
|
|
|
|
* Uses {@link ReorderMode#MULTI_STAGE}
|
|
|
|
* @deprecated Use {@link EditSession#setReorderMode(ReorderMode)} with MULTI_STAGE instead.
|
2010-10-02 21:52:42 +00:00
|
|
|
*/
|
2019-03-28 19:02:37 +00:00
|
|
|
@Deprecated
|
2014-03-31 23:55:58 +00:00
|
|
|
public void enableQueue() {
|
2019-07-18 16:07:31 +00:00
|
|
|
super.enableQueue();
|
2010-10-11 08:22:47 +00:00
|
|
|
}
|
|
|
|
|
2011-01-31 06:15:08 +00:00
|
|
|
/**
|
2018-08-12 14:03:07 +00:00
|
|
|
* Disable the queue. This will close the queue.
|
2011-01-31 06:15:08 +00:00
|
|
|
*/
|
2019-03-28 19:02:37 +00:00
|
|
|
@Deprecated
|
2014-03-31 23:55:58 +00:00
|
|
|
public void disableQueue() {
|
2019-07-18 16:07:31 +00:00
|
|
|
super.disableQueue();
|
2011-07-15 07:00:48 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2010-10-11 08:22:47 +00:00
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Get the mask.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @return mask, may be null
|
2010-10-11 08:22:47 +00:00
|
|
|
*/
|
2014-03-31 23:55:58 +00:00
|
|
|
public Mask getMask() {
|
2019-07-18 06:56:57 +00:00
|
|
|
ExtentTraverser<MaskingExtent> maskingExtent = new ExtentTraverser<>(getExtent()).find(MaskingExtent.class);
|
2018-08-12 14:03:07 +00:00
|
|
|
return maskingExtent != null ? maskingExtent.get().getMask() : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the mask.
|
|
|
|
*
|
|
|
|
* @return mask, may be null
|
|
|
|
*/
|
|
|
|
public Mask getSourceMask() {
|
2019-07-18 06:56:57 +00:00
|
|
|
ExtentTraverser<SourceMaskExtent> maskingExtent = new ExtentTraverser<>(getExtent()).find(SourceMaskExtent.class);
|
2018-08-12 14:03:07 +00:00
|
|
|
return maskingExtent != null ? maskingExtent.get().getMask() : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addTransform(ResettableExtent transform) {
|
2019-07-18 06:56:57 +00:00
|
|
|
checkNotNull(transform);
|
|
|
|
transform.setExtent(getExtent());
|
2019-12-16 09:07:51 +00:00
|
|
|
new ExtentTraverser(this).setNext(transform);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public @Nullable ResettableExtent getTransform() {
|
2019-07-18 06:56:57 +00:00
|
|
|
ExtentTraverser<ResettableExtent> traverser = new ExtentTraverser<>(getExtent()).find(ResettableExtent.class);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (traverser != null) {
|
2019-07-18 06:56:57 +00:00
|
|
|
return traverser.get();
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set a mask.
|
|
|
|
*
|
|
|
|
* @param mask mask or null
|
|
|
|
*/
|
|
|
|
public void setSourceMask(Mask mask) {
|
|
|
|
if (mask == null) {
|
|
|
|
mask = Masks.alwaysTrue();
|
|
|
|
} else {
|
|
|
|
new MaskTraverser(mask).reset(this);
|
|
|
|
}
|
2019-07-18 06:56:57 +00:00
|
|
|
ExtentTraverser<SourceMaskExtent> maskingExtent = new ExtentTraverser<>(getExtent()).find(SourceMaskExtent.class);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (maskingExtent != null && maskingExtent.get() != null) {
|
|
|
|
Mask oldMask = maskingExtent.get().getMask();
|
|
|
|
if (oldMask instanceof ResettableMask) {
|
|
|
|
((ResettableMask) oldMask).reset();
|
|
|
|
}
|
|
|
|
maskingExtent.get().setMask(mask);
|
|
|
|
} else if (mask != Masks.alwaysTrue()) {
|
2019-07-18 06:56:57 +00:00
|
|
|
SourceMaskExtent next = new SourceMaskExtent(getExtent(), mask);
|
2019-12-16 09:07:51 +00:00
|
|
|
new ExtentTraverser(this).setNext(next);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addSourceMask(Mask mask) {
|
|
|
|
checkNotNull(mask);
|
|
|
|
Mask existing = getSourceMask();
|
|
|
|
if (existing != null) {
|
|
|
|
if (existing instanceof MaskIntersection) {
|
2019-07-18 06:56:57 +00:00
|
|
|
Collection<Mask> masks = new HashSet<>(((MaskIntersection) existing).getMasks());
|
|
|
|
masks.add(mask);
|
|
|
|
mask = new MaskIntersection(masks);
|
2018-08-12 14:03:07 +00:00
|
|
|
} else {
|
2019-07-18 06:56:57 +00:00
|
|
|
mask = new MaskIntersection(existing, mask);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
2019-07-18 06:56:57 +00:00
|
|
|
mask = mask.optimize();
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
setSourceMask(mask);
|
2010-10-02 21:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Set a mask.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @param mask mask or null
|
2010-10-02 21:52:42 +00:00
|
|
|
*/
|
2014-03-31 23:55:58 +00:00
|
|
|
public void setMask(Mask mask) {
|
|
|
|
if (mask == null) {
|
2018-08-12 14:03:07 +00:00
|
|
|
mask = Masks.alwaysTrue();
|
2014-03-31 23:55:58 +00:00
|
|
|
} else {
|
2018-08-12 14:03:07 +00:00
|
|
|
new MaskTraverser(mask).reset(this);
|
|
|
|
}
|
2019-07-18 06:56:57 +00:00
|
|
|
ExtentTraverser<MaskingExtent> maskingExtent = new ExtentTraverser<>(getExtent()).find(MaskingExtent.class);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (maskingExtent != null && maskingExtent.get() != null) {
|
|
|
|
Mask oldMask = maskingExtent.get().getMask();
|
|
|
|
if (oldMask instanceof ResettableMask) {
|
|
|
|
((ResettableMask) oldMask).reset();
|
|
|
|
}
|
|
|
|
maskingExtent.get().setMask(mask);
|
|
|
|
} else if (mask != Masks.alwaysTrue()) {
|
2019-12-16 09:07:51 +00:00
|
|
|
addProcessor(new MaskingExtent(getExtent(), mask));
|
2014-06-28 02:55:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-23 07:33:00 +00:00
|
|
|
/**
|
|
|
|
* Get the {@link SurvivalModeExtent}.
|
|
|
|
*
|
|
|
|
* @return the survival simulation extent
|
|
|
|
*/
|
|
|
|
public SurvivalModeExtent getSurvivalExtent() {
|
2019-07-18 06:56:57 +00:00
|
|
|
ExtentTraverser<SurvivalModeExtent> survivalExtent = new ExtentTraverser<>(getExtent()).find(SurvivalModeExtent.class);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (survivalExtent != null) {
|
|
|
|
return survivalExtent.get();
|
|
|
|
} else {
|
2019-10-23 04:23:52 +00:00
|
|
|
SurvivalModeExtent survival = new SurvivalModeExtent(bypassAll, getWorld());
|
|
|
|
bypassAll = survival;
|
2018-08-12 14:03:07 +00:00
|
|
|
return survival;
|
|
|
|
}
|
2014-04-23 07:33:00 +00:00
|
|
|
}
|
|
|
|
|
2010-10-02 21:52:42 +00:00
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Set whether fast mode is enabled.
|
2019-06-04 15:48:30 +00:00
|
|
|
*
|
2014-07-29 18:04:04 +00:00
|
|
|
* <p>Fast mode may skip lighting checks or adjacent block
|
|
|
|
* notification.</p>
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @param enabled true to enable
|
2010-10-02 21:52:42 +00:00
|
|
|
*/
|
2020-02-12 21:18:36 +00:00
|
|
|
@Deprecated
|
2019-06-25 13:00:44 +00:00
|
|
|
public void setFastMode(boolean enabled) {
|
2018-08-12 14:03:07 +00:00
|
|
|
disableHistory(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disable history (or re-enable)
|
|
|
|
*
|
2020-04-23 16:07:01 +00:00
|
|
|
* @param disableHistory whether to enable or disable.
|
2018-08-12 14:03:07 +00:00
|
|
|
*/
|
2020-02-12 21:18:36 +00:00
|
|
|
@Deprecated
|
2018-08-12 14:03:07 +00:00
|
|
|
public void disableHistory(boolean disableHistory) {
|
|
|
|
if (disableHistory) {
|
2019-10-23 04:23:52 +00:00
|
|
|
if (this.history) {
|
|
|
|
disableHistory();
|
|
|
|
this.history = false;
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
2019-10-23 04:23:52 +00:00
|
|
|
} else {
|
|
|
|
if (this.history) {
|
2020-01-03 20:38:30 +00:00
|
|
|
if (this.changeSet == null) {
|
2019-10-23 04:23:52 +00:00
|
|
|
throw new IllegalArgumentException("History was never provided, cannot enable");
|
|
|
|
}
|
2020-01-03 20:38:30 +00:00
|
|
|
enableHistory(this.changeSet);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
2014-04-06 04:49:25 +00:00
|
|
|
}
|
2010-10-02 21:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Return fast mode status.
|
2018-12-20 16:00:27 +00:00
|
|
|
*
|
2014-07-29 18:04:04 +00:00
|
|
|
* <p>Fast mode may skip lighting checks or adjacent block
|
|
|
|
* notification.</p>
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @return true if enabled
|
2010-10-02 21:52:42 +00:00
|
|
|
*/
|
2020-02-12 21:18:36 +00:00
|
|
|
@Deprecated
|
2014-03-31 23:55:58 +00:00
|
|
|
public boolean hasFastMode() {
|
2018-08-12 14:03:07 +00:00
|
|
|
return getChangeSet() == null;
|
2010-10-02 21:52:42 +00:00
|
|
|
}
|
2010-10-04 23:39:35 +00:00
|
|
|
|
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Get the {@link BlockBag} is used.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @return a block bag or null
|
2010-10-04 23:39:35 +00:00
|
|
|
*/
|
2014-03-31 23:55:58 +00:00
|
|
|
public BlockBag getBlockBag() {
|
2018-08-12 14:03:07 +00:00
|
|
|
return this.blockBag;
|
2010-10-04 23:39:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Set a {@link BlockBag} to use.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @param blockBag the block bag to set, or null to use none
|
2010-10-04 23:39:35 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public void setBlockBag(BlockBag blockBag) {
|
2020-01-03 20:38:30 +00:00
|
|
|
//Not Supported in FAWE
|
2019-07-18 06:56:57 +00:00
|
|
|
throw new UnsupportedOperationException("TODO - this is never called anyway");
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
2019-07-18 06:56:57 +00:00
|
|
|
return super.toString() + ":" + getExtent();
|
2010-10-04 23:39:35 +00:00
|
|
|
}
|
2010-10-05 07:40:50 +00:00
|
|
|
|
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Gets the list of missing blocks and clears the list for the next
|
|
|
|
* operation.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @return a map of missing blocks
|
2010-10-05 07:40:50 +00:00
|
|
|
*/
|
2019-07-06 00:46:48 +00:00
|
|
|
public Map<BlockType, Integer> popMissingBlocks() {
|
2018-08-12 14:03:07 +00:00
|
|
|
BlockBag bag = getBlockBag();
|
|
|
|
if (bag != null) {
|
|
|
|
bag.flushChanges();
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
Map<BlockType, Integer> missingBlocks;
|
2018-08-12 14:03:07 +00:00
|
|
|
ChangeSet changeSet = getChangeSet();
|
|
|
|
|
|
|
|
|
|
|
|
if (changeSet instanceof BlockBagChangeSet) {
|
|
|
|
missingBlocks = ((BlockBagChangeSet) changeSet).popMissing();
|
|
|
|
} else {
|
2019-07-18 06:56:57 +00:00
|
|
|
ExtentTraverser<BlockBagExtent> find = new ExtentTraverser<>(getExtent()).find(BlockBagExtent.class);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (find != null && find.get() != null) {
|
|
|
|
missingBlocks = find.get().popMissing();
|
|
|
|
} else {
|
|
|
|
missingBlocks = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (missingBlocks != null && !missingBlocks.isEmpty()) {
|
|
|
|
StringBuilder str = new StringBuilder();
|
|
|
|
int size = missingBlocks.size();
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
for (Map.Entry<BlockType, Integer> entry : missingBlocks.entrySet()) {
|
|
|
|
BlockType type = entry.getKey();
|
|
|
|
int amount = entry.getValue();
|
|
|
|
str.append((type.getName())).append((amount != 1 ? "x" + amount : ""));
|
|
|
|
++i;
|
|
|
|
if (i != size) {
|
|
|
|
str.append(", ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-16 09:01:41 +00:00
|
|
|
player.print(Caption.of("fawe.error.worldedit.some.fails.blockbag", str.toString()));
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-10 08:32:21 +00:00
|
|
|
return Collections.emptyMap();
|
2010-10-05 07:40:50 +00:00
|
|
|
}
|
2011-08-07 00:40:48 +00:00
|
|
|
|
2018-10-21 01:54:58 +00:00
|
|
|
/**
|
|
|
|
* Returns chunk batching status.
|
|
|
|
*
|
|
|
|
* @return whether chunk batching is enabled
|
|
|
|
*/
|
2018-10-04 17:37:58 +00:00
|
|
|
public boolean isBatchingChunks() {
|
2019-04-03 05:53:34 +00:00
|
|
|
return false;
|
2018-10-04 17:37:58 +00:00
|
|
|
}
|
|
|
|
|
2018-10-21 01:54:58 +00:00
|
|
|
/**
|
|
|
|
* Enable or disable chunk batching. Disabling will
|
|
|
|
* {@linkplain #flushSession() flush the session}.
|
|
|
|
*
|
|
|
|
* @param batchingChunks {@code true} to enable, {@code false} to disable
|
|
|
|
*/
|
2018-10-04 17:37:58 +00:00
|
|
|
public void setBatchingChunks(boolean batchingChunks) {
|
2019-07-18 16:07:31 +00:00
|
|
|
if (batchingChunks) {
|
|
|
|
enableQueue();
|
|
|
|
} else {
|
|
|
|
disableQueue();
|
|
|
|
}
|
2018-10-04 17:37:58 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
/**
|
|
|
|
* Disable all buffering extents.
|
|
|
|
*
|
2019-06-25 13:00:44 +00:00
|
|
|
* @see #setReorderMode(ReorderMode)
|
2019-06-04 15:48:30 +00:00
|
|
|
* @see #setBatchingChunks(boolean)
|
|
|
|
*/
|
2018-10-21 01:54:58 +00:00
|
|
|
public void disableBuffering() {
|
2019-07-18 16:07:31 +00:00
|
|
|
disableQueue();
|
2018-10-21 01:54:58 +00:00
|
|
|
}
|
|
|
|
|
2019-09-05 05:50:14 +00:00
|
|
|
/**
|
|
|
|
* Check if this session will tick the watchdog.
|
|
|
|
*
|
|
|
|
* @return {@code true} if any watchdog extent is enabled
|
|
|
|
*/
|
|
|
|
public boolean isTickingWatchdog() {
|
|
|
|
return watchdogExtents.stream().anyMatch(WatchdogTickingExtent::isEnabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set all watchdog extents to the given mode.
|
|
|
|
*/
|
|
|
|
public void setTickingWatchdog(boolean active) {
|
|
|
|
for (WatchdogTickingExtent extent : watchdogExtents) {
|
|
|
|
extent.setEnabled(active);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-04 17:30:45 +00:00
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Get the number of blocks changed, including repeated block changes.
|
2019-06-04 15:48:30 +00:00
|
|
|
*
|
2014-07-29 18:04:04 +00:00
|
|
|
* <p>This number may not be accurate.</p>
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @return the number of block changes
|
2011-06-04 17:30:45 +00:00
|
|
|
*/
|
2014-03-31 23:55:58 +00:00
|
|
|
public int getBlockChangeCount() {
|
2018-08-12 14:03:07 +00:00
|
|
|
return this.changes;
|
2011-06-04 17:30:45 +00:00
|
|
|
}
|
2011-08-07 00:40:48 +00:00
|
|
|
|
2014-07-17 07:21:13 +00:00
|
|
|
@Override
|
2019-06-25 13:00:44 +00:00
|
|
|
public boolean setBiome(BlockVector2 position, BiomeType biome) {
|
2018-08-12 14:03:07 +00:00
|
|
|
this.changes++;
|
2019-07-18 06:56:57 +00:00
|
|
|
return this.getExtent().setBiome(position, biome);
|
2014-07-17 07:21:13 +00:00
|
|
|
}
|
|
|
|
|
2014-03-31 23:55:58 +00:00
|
|
|
@Override
|
2019-04-03 05:53:34 +00:00
|
|
|
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
2018-08-12 14:03:07 +00:00
|
|
|
this.changes++;
|
2019-07-18 06:56:57 +00:00
|
|
|
return this.getExtent().setBiome(x, y, z, biome);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
|
2011-11-01 13:54:32 +00:00
|
|
|
/**
|
2018-07-02 14:45:38 +00:00
|
|
|
* Returns the highest solid 'terrain' block.
|
2011-11-01 13:54:32 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @param x the X coordinate
|
|
|
|
* @param z the Z coordinate
|
2011-11-01 13:54:32 +00:00
|
|
|
* @param minY minimal height
|
|
|
|
* @param maxY maximal height
|
|
|
|
* @return height of highest block found or 'minY'
|
|
|
|
*/
|
2018-12-23 16:19:33 +00:00
|
|
|
public int getHighestTerrainBlock(int x, int z, int minY, int maxY) {
|
2019-06-28 14:01:51 +00:00
|
|
|
for (int y = maxY; y >= minY; --y) {
|
|
|
|
if (getBlock(x, y, z).getBlockType().getMaterial().isMovementBlocker()) {
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return minY;
|
2019-03-28 19:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the highest solid 'terrain' block.
|
|
|
|
*
|
|
|
|
* @param x the X coordinate
|
|
|
|
* @param z the Z coordinate
|
|
|
|
* @param minY minimal height
|
|
|
|
* @param maxY maximal height
|
|
|
|
* @param filter a mask of blocks to consider, or null to consider any solid (movement-blocking) block
|
|
|
|
* @return height of highest block found or 'minY'
|
|
|
|
*/
|
|
|
|
public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) {
|
2018-12-23 16:19:33 +00:00
|
|
|
for (int y = maxY; y >= minY; --y) {
|
2020-01-04 10:11:13 +00:00
|
|
|
if (filter.test(getExtent(), mutablebv.setComponents(x, y, z))) {
|
2018-12-23 16:19:33 +00:00
|
|
|
return y;
|
|
|
|
}
|
|
|
|
}
|
2019-09-14 03:05:16 +00:00
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
return minY;
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
public BlockType getBlockType(int x, int y, int z) {
|
2019-07-18 06:56:57 +00:00
|
|
|
return getBlock(x, y, z).getBlockType();
|
2011-11-01 13:54:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-03-31 23:55:58 +00:00
|
|
|
* Set a block, bypassing both history and block re-ordering.
|
2011-11-01 13:54:32 +00:00
|
|
|
*
|
2014-03-31 23:55:58 +00:00
|
|
|
* @param position the position to set the block at
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param block the block
|
|
|
|
* @param stage the level
|
2014-03-31 23:55:58 +00:00
|
|
|
* @return whether the block changed
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws WorldEditException thrown on a set error
|
2014-03-31 23:55:58 +00:00
|
|
|
*/
|
2020-04-23 16:07:01 +00:00
|
|
|
@Deprecated
|
2018-12-27 00:39:10 +00:00
|
|
|
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, Stage stage) throws WorldEditException {
|
2020-02-29 22:42:00 +00:00
|
|
|
if (position.getBlockY() < 0 || position.getBlockY() > 255) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
this.changes++;
|
2014-04-04 01:24:06 +00:00
|
|
|
switch (stage) {
|
|
|
|
case BEFORE_HISTORY:
|
2019-07-18 06:56:57 +00:00
|
|
|
return this.getExtent().setBlock(position, block);
|
2014-04-04 01:24:06 +00:00
|
|
|
case BEFORE_CHANGE:
|
2019-06-30 18:56:32 +00:00
|
|
|
return bypassHistory.setBlock(position, block);
|
2014-04-04 01:24:06 +00:00
|
|
|
case BEFORE_REORDER:
|
2019-06-30 18:56:32 +00:00
|
|
|
return bypassAll.setBlock(position, block);
|
2014-03-31 23:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
throw new RuntimeException("New enum entry added that is unhandled here");
|
|
|
|
}
|
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
/**
|
|
|
|
* Set a block, bypassing both history and block re-ordering.
|
|
|
|
*
|
|
|
|
* @param position the position to set the block at
|
|
|
|
* @param block the block
|
|
|
|
* @return whether the block changed
|
|
|
|
*/
|
2020-04-23 16:07:01 +00:00
|
|
|
@Deprecated
|
2018-12-27 00:39:10 +00:00
|
|
|
public <B extends BlockStateHolder<B>> boolean rawSetBlock(BlockVector3 position, B block) {
|
2020-02-29 22:42:00 +00:00
|
|
|
if (position.getBlockY() < 0 || position.getBlockY() > 255) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-03 05:53:34 +00:00
|
|
|
this.changes++;
|
2018-12-23 16:19:33 +00:00
|
|
|
try {
|
2019-06-30 18:56:32 +00:00
|
|
|
return bypassAll.setBlock(position, block);
|
2019-03-28 19:02:37 +00:00
|
|
|
} catch (WorldEditException e) {
|
2018-12-23 16:19:33 +00:00
|
|
|
throw new RuntimeException("Unexpected exception", e);
|
|
|
|
}
|
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2011-11-01 13:54:32 +00:00
|
|
|
/**
|
2018-08-12 14:03:07 +00:00
|
|
|
* Set a block, bypassing history but still utilizing block re-ordering.
|
2014-03-31 23:55:58 +00:00
|
|
|
*
|
|
|
|
* @param position the position to set the block at
|
2018-12-23 16:19:33 +00:00
|
|
|
* @param block the block
|
2014-03-31 23:55:58 +00:00
|
|
|
* @return whether the block changed
|
2011-11-01 13:54:32 +00:00
|
|
|
*/
|
2018-12-27 00:39:10 +00:00
|
|
|
public <B extends BlockStateHolder<B>> boolean smartSetBlock(BlockVector3 position, B block) {
|
2020-02-29 22:42:00 +00:00
|
|
|
if (position.getBlockY() < 0 || position.getBlockY() > 255) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-03 05:53:34 +00:00
|
|
|
this.changes++;
|
2018-08-12 14:03:07 +00:00
|
|
|
try {
|
2018-12-23 16:19:33 +00:00
|
|
|
return setBlock(position, block, Stage.BEFORE_REORDER);
|
|
|
|
} catch (WorldEditException e) {
|
2018-08-12 14:03:07 +00:00
|
|
|
throw new RuntimeException("Unexpected exception", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-23 16:07:01 +00:00
|
|
|
@Override @Deprecated
|
2018-12-27 00:39:10 +00:00
|
|
|
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block) throws MaxChangedBlocksException {
|
2020-02-29 22:42:00 +00:00
|
|
|
if (position.getBlockY() < 0 || position.getBlockY() > 255) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-17 09:21:45 +00:00
|
|
|
this.changes++;
|
2014-03-31 23:55:58 +00:00
|
|
|
try {
|
2019-07-18 06:56:57 +00:00
|
|
|
return this.getExtent().setBlock(position, block);
|
2018-08-12 14:03:07 +00:00
|
|
|
} catch (MaxChangedBlocksException e) {
|
|
|
|
throw e;
|
2014-03-31 23:55:58 +00:00
|
|
|
} catch (WorldEditException e) {
|
|
|
|
throw new RuntimeException("Unexpected exception", e);
|
|
|
|
}
|
2011-11-01 13:54:32 +00:00
|
|
|
}
|
|
|
|
|
2019-06-30 18:56:32 +00:00
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
@Override
|
2020-02-29 22:42:00 +00:00
|
|
|
public <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B block) {
|
|
|
|
if (y < 0 || y > 255) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
this.changes++;
|
2014-03-31 23:55:58 +00:00
|
|
|
try {
|
2019-07-18 06:56:57 +00:00
|
|
|
return this.getExtent().setBlock(x, y, z, block);
|
2014-03-31 23:55:58 +00:00
|
|
|
} catch (WorldEditException e) {
|
|
|
|
throw new RuntimeException("Unexpected exception", e);
|
|
|
|
}
|
2011-11-01 13:54:32 +00:00
|
|
|
}
|
|
|
|
|
2019-06-30 18:56:32 +00:00
|
|
|
/**
|
2019-11-21 07:57:32 +00:00
|
|
|
* Sets the block at a position, subject to both history and block re-ordering.
|
2019-06-30 18:56:32 +00:00
|
|
|
*
|
|
|
|
* @param x the x coordinate
|
|
|
|
* @param y the y coordinate
|
|
|
|
* @param z the z coordinate
|
|
|
|
* @param pattern a pattern to use
|
|
|
|
* @return Whether the block changed -- not entirely dependable
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
2018-08-12 14:03:07 +00:00
|
|
|
public boolean setBlock(int x, int y, int z, Pattern pattern) {
|
2020-02-29 22:42:00 +00:00
|
|
|
if (y < 0 || y > 255) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
this.changes++;
|
2014-03-31 23:55:58 +00:00
|
|
|
try {
|
2019-03-31 16:09:20 +00:00
|
|
|
BlockVector3 bv = mutablebv.setComponents(x, y, z);
|
2019-07-18 06:56:57 +00:00
|
|
|
return pattern.apply(getExtent(), bv, bv);
|
2014-03-31 23:55:58 +00:00
|
|
|
} catch (WorldEditException e) {
|
|
|
|
throw new RuntimeException("Unexpected exception", e);
|
|
|
|
}
|
2011-11-01 13:54:32 +00:00
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
|
2019-06-30 18:56:32 +00:00
|
|
|
/**
|
|
|
|
* Sets the block at a position, subject to both history and block re-ordering.
|
|
|
|
*
|
|
|
|
* @param position the position
|
|
|
|
* @param pattern a pattern to use
|
|
|
|
* @return Whether the block changed -- not entirely dependable
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
public boolean setBlock(BlockVector3 position, Pattern pattern) throws MaxChangedBlocksException {
|
2020-02-29 22:42:00 +00:00
|
|
|
if (position.getBlockY() < 0 || position.getBlockY() > 255) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
this.changes++;
|
|
|
|
try {
|
2019-07-18 06:56:57 +00:00
|
|
|
return pattern.apply(this.getExtent(), position, position);
|
2018-08-12 14:03:07 +00:00
|
|
|
} catch (WorldEditException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-31 23:55:58 +00:00
|
|
|
/**
|
|
|
|
* Restores all blocks to their initial state.
|
|
|
|
*
|
2014-04-01 01:49:39 +00:00
|
|
|
* @param editSession a new {@link EditSession} to perform the undo in
|
2014-03-31 23:55:58 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public void undo(EditSession editSession) {
|
|
|
|
UndoContext context = new UndoContext();
|
2018-08-12 14:03:07 +00:00
|
|
|
context.setExtent(editSession.bypassAll);
|
|
|
|
ChangeSet changeSet = getChangeSet();
|
2019-07-18 16:07:31 +00:00
|
|
|
setChangeSet(null);
|
2018-08-12 14:03:07 +00:00
|
|
|
Operations.completeBlindly(ChangeSetExecutor.create(changeSet, context, ChangeSetExecutor.Type.UNDO, editSession.getBlockBag(), editSession.getLimit().INVENTORY_MODE));
|
|
|
|
flushQueue();
|
|
|
|
editSession.changes = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setBlocks(ChangeSet changeSet, ChangeSetExecutor.Type type) {
|
|
|
|
final UndoContext context = new UndoContext();
|
2019-10-23 04:23:52 +00:00
|
|
|
context.setExtent(bypassAll);
|
2018-08-12 14:03:07 +00:00
|
|
|
Operations.completeBlindly(ChangeSetExecutor.create(changeSet, context, type, getBlockBag(), getLimit().INVENTORY_MODE));
|
|
|
|
flushQueue();
|
|
|
|
changes = 1;
|
2014-03-31 23:55:58 +00:00
|
|
|
}
|
2011-10-24 02:04:45 +00:00
|
|
|
|
2014-03-31 23:55:58 +00:00
|
|
|
/**
|
|
|
|
* Sets to new state.
|
|
|
|
*
|
2014-04-01 01:49:39 +00:00
|
|
|
* @param editSession a new {@link EditSession} to perform the redo in
|
2014-03-31 23:55:58 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public void redo(EditSession editSession) {
|
|
|
|
UndoContext context = new UndoContext();
|
2018-08-12 14:03:07 +00:00
|
|
|
context.setExtent(editSession.bypassAll);
|
|
|
|
ChangeSet changeSet = getChangeSet();
|
2019-07-18 16:07:31 +00:00
|
|
|
setChangeSet(null);
|
2018-08-12 14:03:07 +00:00
|
|
|
Operations.completeBlindly(ChangeSetExecutor.create(changeSet, context, ChangeSetExecutor.Type.REDO, editSession.getBlockBag(), editSession.getLimit().INVENTORY_MODE));
|
|
|
|
flushQueue();
|
|
|
|
editSession.changes = 1;
|
2014-03-31 23:55:58 +00:00
|
|
|
}
|
2011-10-24 02:04:45 +00:00
|
|
|
|
2014-03-31 23:55:58 +00:00
|
|
|
/**
|
|
|
|
* Get the number of changed blocks.
|
|
|
|
*
|
|
|
|
* @return the number of changes
|
|
|
|
*/
|
|
|
|
public int size() {
|
2019-03-28 19:02:37 +00:00
|
|
|
return getBlockChangeCount();
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setSize(int size) {
|
|
|
|
this.changes = size;
|
2014-03-31 23:55:58 +00:00
|
|
|
}
|
2011-10-24 02:04:45 +00:00
|
|
|
|
2019-02-27 14:01:07 +00:00
|
|
|
/**
|
|
|
|
* Closing an EditSession {@linkplain #flushSession() flushes its buffers}.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public void close() {
|
|
|
|
flushSession();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Communicate to the EditSession that all block changes are complete,
|
|
|
|
* and that it should apply them to the world.
|
|
|
|
*/
|
|
|
|
public void flushSession() {
|
2019-04-03 11:28:57 +00:00
|
|
|
flushQueue();
|
2019-02-27 14:01:07 +00:00
|
|
|
}
|
|
|
|
|
2014-03-31 23:55:58 +00:00
|
|
|
/**
|
|
|
|
* Finish off the queue.
|
|
|
|
*/
|
|
|
|
public void flushQueue() {
|
2014-04-01 23:21:27 +00:00
|
|
|
Operations.completeBlindly(commit());
|
2018-08-12 14:03:07 +00:00
|
|
|
// Check fails
|
|
|
|
FaweLimit used = getLimitUsed();
|
|
|
|
if (used.MAX_FAILS > 0) {
|
|
|
|
if (used.MAX_CHANGES > 0 || used.MAX_ENTITIES > 0) {
|
2019-12-16 09:01:41 +00:00
|
|
|
player.print(Caption.of("fawe.error.worldedit.some.fails", used.MAX_FAILS));
|
2019-07-18 06:56:57 +00:00
|
|
|
} else if (new ExtentTraverser<>(getExtent()).findAndGet(FaweRegionExtent.class) != null){
|
2019-11-21 19:56:10 +00:00
|
|
|
player.printError(TranslatableComponent.of("fawe.cancel.worldedit.cancel.reason.outside.region"));
|
2018-08-12 14:03:07 +00:00
|
|
|
} else {
|
2019-11-21 19:56:10 +00:00
|
|
|
player.printError(TranslatableComponent.of("fawe.cancel.worldedit.cancel.reason.outside.level"));
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Reset limit
|
|
|
|
limit.set(originalLimit);
|
|
|
|
// Enqueue it
|
|
|
|
if (getChangeSet() != null) {
|
|
|
|
if (Settings.IMP.HISTORY.COMBINE_STAGES) {
|
2020-01-06 08:36:16 +00:00
|
|
|
((AbstractChangeSet) getChangeSet()).closeAsync();
|
2018-08-12 14:03:07 +00:00
|
|
|
} else {
|
2019-11-16 00:20:14 +00:00
|
|
|
try {
|
2020-04-23 16:07:01 +00:00
|
|
|
getChangeSet().close();
|
2019-11-16 00:20:14 +00:00
|
|
|
} catch (IOException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-31 23:55:58 +00:00
|
|
|
}
|
2011-10-24 02:04:45 +00:00
|
|
|
|
2019-09-22 01:00:45 +00:00
|
|
|
public <B extends BlockStateHolder<B>> int fall(final Region region, boolean fullHeight, final B replace) {
|
2018-08-12 14:03:07 +00:00
|
|
|
FlatRegion flat = asFlatRegion(region);
|
|
|
|
final int startPerformY = region.getMinimumPoint().getBlockY();
|
|
|
|
final int startCheckY = fullHeight ? 0 : startPerformY;
|
|
|
|
final int endY = region.getMaximumPoint().getBlockY();
|
2020-04-23 16:07:01 +00:00
|
|
|
RegionVisitor visitor = new RegionVisitor(flat, pos -> {
|
|
|
|
int x = pos.getX();
|
|
|
|
int z = pos.getZ();
|
|
|
|
int freeSpot = startCheckY;
|
|
|
|
for (int y = startCheckY; y <= endY; y++) {
|
|
|
|
if (y < startPerformY) {
|
|
|
|
if (!getBlockType(x, y, z).getMaterial().isAir()) {
|
|
|
|
freeSpot = y + 1;
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
2020-04-23 16:07:01 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
BlockType block = getBlockType(x, y, z);
|
|
|
|
if (!block.getMaterial().isAir()) {
|
|
|
|
if (freeSpot != y) {
|
|
|
|
setBlock(x, freeSpot, z, block);
|
|
|
|
setBlock(x, y, z, replace);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
2020-04-23 16:07:01 +00:00
|
|
|
freeSpot++;
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
2020-04-23 16:07:01 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
2018-08-12 14:03:07 +00:00
|
|
|
Operations.completeBlindly(visitor);
|
|
|
|
return this.changes;
|
2010-10-05 07:40:50 +00:00
|
|
|
}
|
2010-10-11 08:22:47 +00:00
|
|
|
|
2020-05-07 16:29:18 +00:00
|
|
|
@Override
|
|
|
|
public <B extends BlockStateHolder<B>> int setBlocks(Region region, B block) throws MaxChangedBlocksException {
|
|
|
|
return this.changes = super.setBlocks(region, block);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException {
|
|
|
|
return this.changes = super.setBlocks(region, pattern);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public <B extends BlockStateHolder<B>> int replaceBlocks(Region region, Set<BaseBlock> filter, B replacement) throws MaxChangedBlocksException {
|
|
|
|
return this.changes = super.replaceBlocks(region, filter, replacement);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int replaceBlocks(Region region, Set<BaseBlock> filter, Pattern pattern) throws MaxChangedBlocksException {
|
|
|
|
return this.changes = super.replaceBlocks(region, filter, pattern);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException {
|
|
|
|
return this.changes = super.replaceBlocks(region, mask, pattern);
|
|
|
|
}
|
|
|
|
|
2010-10-11 08:22:47 +00:00
|
|
|
/**
|
|
|
|
* Fills an area recursively in the X/Z directions.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2019-08-06 15:29:09 +00:00
|
|
|
* @param origin the location to start from
|
|
|
|
* @param pattern the block to fill with
|
|
|
|
* @param radius the radius of the spherical area to fill
|
|
|
|
* @param depth the maximum depth, starting from the origin
|
2018-08-12 14:03:07 +00:00
|
|
|
* @param direction the direction to fill
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
2019-07-06 00:46:48 +00:00
|
|
|
public int fillDirection(final BlockVector3 origin, final Pattern pattern, final double radius, final int depth, BlockVector3 direction) throws MaxChangedBlocksException {
|
2018-08-12 14:03:07 +00:00
|
|
|
checkNotNull(origin);
|
|
|
|
checkNotNull(pattern);
|
|
|
|
checkArgument(radius >= 0, "radius >= 0");
|
|
|
|
checkArgument(depth >= 1, "depth >= 1");
|
2018-10-19 20:13:32 +00:00
|
|
|
if (direction.equals(BlockVector3.at(0, -1, 0))) {
|
2018-08-12 14:03:07 +00:00
|
|
|
return fillXZ(origin, pattern, radius, depth, false);
|
|
|
|
}
|
2020-01-04 10:11:13 +00:00
|
|
|
Mask mask = new MaskIntersection(new RegionMask(new EllipsoidRegion(null, origin, Vector3.at(radius, radius, radius))), Masks.negate(new ExistingBlockMask(EditSession.this))).withExtent(getExtent());
|
2018-08-12 14:03:07 +00:00
|
|
|
// Want to replace blocks
|
|
|
|
final BlockReplace replace = new BlockReplace(EditSession.this, pattern);
|
|
|
|
|
|
|
|
// Pick how we're going to visit blocks
|
2019-07-18 06:56:57 +00:00
|
|
|
RecursiveVisitor visitor = new DirectionalVisitor(mask, replace, origin, direction, (int) (radius * 2 + 1));
|
2018-08-12 14:03:07 +00:00
|
|
|
|
|
|
|
// Start at the origin
|
|
|
|
visitor.visit(origin);
|
|
|
|
|
|
|
|
// Execute
|
|
|
|
Operations.completeBlindly(visitor);
|
|
|
|
return this.changes = visitor.getAffected();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fills an area recursively in the X/Z directions.
|
|
|
|
*
|
2019-06-04 15:48:30 +00:00
|
|
|
* @param origin the location to start from
|
|
|
|
* @param block the block to fill with
|
|
|
|
* @param radius the radius of the spherical area to fill
|
|
|
|
* @param depth the maximum depth, starting from the origin
|
2014-03-27 06:10:47 +00:00
|
|
|
* @param recursive whether a breadth-first search should be performed
|
2010-10-11 15:56:19 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-27 06:10:47 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-11 08:22:47 +00:00
|
|
|
*/
|
2018-12-27 00:39:10 +00:00
|
|
|
public <B extends BlockStateHolder<B>> int fillXZ(BlockVector3 origin, B block, double radius, int depth, boolean recursive) throws MaxChangedBlocksException {
|
2019-09-02 19:22:43 +00:00
|
|
|
return fillXZ(origin, (Pattern) block, radius, depth, recursive);
|
2010-10-11 08:22:47 +00:00
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2010-11-16 22:07:52 +00:00
|
|
|
/**
|
|
|
|
* Fills an area recursively in the X/Z directions.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2019-06-04 15:48:30 +00:00
|
|
|
* @param origin the origin to start the fill from
|
|
|
|
* @param pattern the pattern to fill with
|
|
|
|
* @param radius the radius of the spherical area to fill, with 0 as the smallest radius
|
|
|
|
* @param depth the maximum depth, starting from the origin, with 1 as the smallest depth
|
2014-03-27 06:10:47 +00:00
|
|
|
* @param recursive whether a breadth-first search should be performed
|
2010-11-16 22:07:52 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-27 06:10:47 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-11-16 22:07:52 +00:00
|
|
|
*/
|
2019-03-28 19:02:37 +00:00
|
|
|
public int fillXZ(BlockVector3 origin, Pattern pattern, double radius, int depth, boolean recursive) throws MaxChangedBlocksException {
|
2014-03-28 07:41:00 +00:00
|
|
|
checkNotNull(origin);
|
|
|
|
checkNotNull(pattern);
|
2014-03-28 03:48:36 +00:00
|
|
|
checkArgument(radius >= 0, "radius >= 0");
|
|
|
|
checkArgument(depth >= 1, "depth >= 1");
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2020-01-04 10:11:13 +00:00
|
|
|
Mask mask = new MaskIntersection(
|
2019-06-04 15:48:30 +00:00
|
|
|
new RegionMask(new EllipsoidRegion(null, origin, Vector3.at(radius, radius, radius))),
|
|
|
|
new BoundedHeightMask(
|
2019-06-25 13:00:44 +00:00
|
|
|
Math.max(origin.getBlockY() - depth + 1, getMinimumPoint().getBlockY()),
|
2019-11-14 17:25:37 +00:00
|
|
|
Math.min(getMaxY(), origin.getBlockY())),
|
2020-01-04 10:11:13 +00:00
|
|
|
Masks.negate(new ExistingBlockMask(this))).withExtent(getExtent());
|
2014-03-27 06:10:47 +00:00
|
|
|
// Want to replace blocks
|
2019-03-28 19:02:37 +00:00
|
|
|
BlockReplace replace = new BlockReplace(this, pattern);
|
2010-11-16 22:07:52 +00:00
|
|
|
|
2014-03-27 06:10:47 +00:00
|
|
|
// Pick how we're going to visit blocks
|
|
|
|
RecursiveVisitor visitor;
|
|
|
|
if (recursive) {
|
2019-07-18 06:56:57 +00:00
|
|
|
visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1));
|
2014-03-27 06:10:47 +00:00
|
|
|
} else {
|
2019-07-18 06:56:57 +00:00
|
|
|
visitor = new DownwardVisitor(mask, replace, origin.getBlockY(), (int) (radius * 2 + 1));
|
2010-11-16 22:07:52 +00:00
|
|
|
}
|
|
|
|
|
2014-03-27 06:10:47 +00:00
|
|
|
// Start at the origin
|
|
|
|
visitor.visit(origin);
|
2010-11-16 22:07:52 +00:00
|
|
|
|
2014-03-27 06:10:47 +00:00
|
|
|
// Execute
|
2018-08-12 14:03:07 +00:00
|
|
|
Operations.completeBlindly(visitor);
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
return this.changes = visitor.getAffected();
|
2010-11-16 22:07:52 +00:00
|
|
|
}
|
|
|
|
|
2010-10-11 08:22:47 +00:00
|
|
|
/**
|
2014-03-28 02:57:24 +00:00
|
|
|
* Remove a cuboid above the given position with a given apothem and a given height.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-28 02:57:24 +00:00
|
|
|
* @param position base position
|
2019-06-04 15:48:30 +00:00
|
|
|
* @param apothem an apothem of the cuboid (on the XZ plane), where the minimum is 1
|
|
|
|
* @param height the height of the cuboid, where the minimum is 1
|
2010-10-11 15:56:19 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-28 03:48:36 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-11 08:22:47 +00:00
|
|
|
*/
|
2018-12-23 16:19:33 +00:00
|
|
|
public int removeAbove(BlockVector3 position, int apothem, int height) throws MaxChangedBlocksException {
|
2014-03-28 07:41:00 +00:00
|
|
|
checkNotNull(position);
|
2014-03-28 02:57:24 +00:00
|
|
|
checkArgument(apothem >= 1, "apothem >= 1");
|
|
|
|
checkArgument(height >= 1, "height >= 1");
|
2010-10-11 08:22:47 +00:00
|
|
|
|
2019-04-03 03:36:32 +00:00
|
|
|
Region region = new CuboidRegion(
|
|
|
|
getWorld(), // Causes clamping of Y range
|
2019-06-04 15:48:30 +00:00
|
|
|
position.add(-apothem + 1, 0, -apothem + 1),
|
|
|
|
position.add(apothem - 1, height - 1, apothem - 1));
|
2019-07-28 22:20:00 +00:00
|
|
|
return setBlocks(region, BlockTypes.AIR.getDefaultState());
|
2010-10-11 08:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-03-28 02:57:24 +00:00
|
|
|
* Remove a cuboid below the given position with a given apothem and a given height.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-28 02:57:24 +00:00
|
|
|
* @param position base position
|
2019-06-04 15:48:30 +00:00
|
|
|
* @param apothem an apothem of the cuboid (on the XZ plane), where the minimum is 1
|
|
|
|
* @param height the height of the cuboid, where the minimum is 1
|
2010-10-11 15:56:19 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-28 03:48:36 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-11 08:22:47 +00:00
|
|
|
*/
|
2018-12-23 16:19:33 +00:00
|
|
|
public int removeBelow(BlockVector3 position, int apothem, int height) throws MaxChangedBlocksException {
|
2014-03-28 07:41:00 +00:00
|
|
|
checkNotNull(position);
|
2014-03-28 02:57:24 +00:00
|
|
|
checkArgument(apothem >= 1, "apothem >= 1");
|
|
|
|
checkArgument(height >= 1, "height >= 1");
|
2010-11-06 22:09:32 +00:00
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
Region region = new CuboidRegion(
|
|
|
|
getWorld(), // Causes clamping of Y range
|
|
|
|
position.add(-apothem + 1, 0, -apothem + 1),
|
|
|
|
position.add(apothem - 1, -height + 1, apothem - 1));
|
2019-09-22 01:00:45 +00:00
|
|
|
return setBlocks(region, BlockTypes.AIR.getDefaultState());
|
2010-10-11 08:22:47 +00:00
|
|
|
}
|
|
|
|
|
2019-06-25 13:00:44 +00:00
|
|
|
/**
|
|
|
|
* Remove blocks of a certain type nearby a given position.
|
|
|
|
*
|
|
|
|
* @param position center position of cuboid
|
2019-11-19 21:23:47 +00:00
|
|
|
* @param mask the mask to match
|
2019-06-25 13:00:44 +00:00
|
|
|
* @param apothem an apothem of the cuboid, where the minimum is 1
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
2019-11-19 21:23:47 +00:00
|
|
|
public int removeNear(BlockVector3 position, Mask mask, int apothem) throws MaxChangedBlocksException {
|
2018-12-23 16:19:33 +00:00
|
|
|
checkNotNull(position);
|
|
|
|
checkArgument(apothem >= 1, "apothem >= 1");
|
|
|
|
|
|
|
|
BlockVector3 adjustment = BlockVector3.ONE.multiply(apothem - 1);
|
|
|
|
Region region = new CuboidRegion(
|
|
|
|
getWorld(), // Causes clamping of Y range
|
|
|
|
position.add(adjustment.multiply(-1)),
|
|
|
|
position.add(adjustment));
|
2019-09-14 03:05:16 +00:00
|
|
|
return replaceBlocks(region, mask, BlockTypes.AIR.getDefaultState());
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:29:09 +00:00
|
|
|
/**
|
|
|
|
* Sets the blocks at the center of the given region to the given pattern.
|
|
|
|
* If the center sits between two blocks on a certain axis, then two blocks
|
|
|
|
* will be placed to mark the center.
|
|
|
|
*
|
|
|
|
* @param region the region to find the center of
|
|
|
|
* @param pattern the replacement pattern
|
|
|
|
* @return the number of blocks placed
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
public int center(Region region, Pattern pattern) throws MaxChangedBlocksException {
|
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(pattern);
|
|
|
|
|
|
|
|
Vector3 center = region.getCenter();
|
|
|
|
Region centerRegion = new CuboidRegion(
|
2019-09-14 03:05:16 +00:00
|
|
|
getWorld(), // Causes clamping of Y range
|
|
|
|
BlockVector3.at(((int) center.getX()), ((int) center.getY()), ((int) center.getZ())),
|
|
|
|
BlockVector3.at(
|
|
|
|
MathUtils.roundHalfUp(center.getX()),
|
|
|
|
MathUtils.roundHalfUp(center.getY()),
|
|
|
|
MathUtils.roundHalfUp(center.getZ())));
|
2019-08-06 15:29:09 +00:00
|
|
|
return setBlocks(centerRegion, pattern);
|
|
|
|
}
|
|
|
|
|
2010-10-11 08:22:47 +00:00
|
|
|
/**
|
2014-03-28 07:40:04 +00:00
|
|
|
* Make the faces of the given region as if it was a {@link CuboidRegion}.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-28 07:40:04 +00:00
|
|
|
* @param region the region
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param block the block to place
|
2010-10-11 15:56:19 +00:00
|
|
|
* @return number of blocks affected
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-11 08:22:47 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public <B extends BlockStateHolder<B>> int makeCuboidFaces(Region region, B block) throws MaxChangedBlocksException {
|
2019-07-06 00:46:48 +00:00
|
|
|
return makeCuboidFaces(region, block);
|
2010-10-11 08:22:47 +00:00
|
|
|
}
|
2011-08-24 22:27:18 +00:00
|
|
|
|
|
|
|
/**
|
2014-03-28 07:40:04 +00:00
|
|
|
* Make the faces of the given region as if it was a {@link CuboidRegion}.
|
2011-08-22 06:55:50 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param region the region
|
2014-03-28 07:40:04 +00:00
|
|
|
* @param pattern the pattern to place
|
2011-08-22 06:55:50 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-28 07:40:04 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2011-08-22 06:55:50 +00:00
|
|
|
*/
|
2019-06-25 13:00:44 +00:00
|
|
|
public int makeCuboidFaces(Region region, Pattern pattern) throws MaxChangedBlocksException {
|
2014-03-28 07:40:04 +00:00
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(pattern);
|
2011-08-22 06:55:50 +00:00
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
CuboidRegion cuboid = CuboidRegion.makeCuboid(region);
|
|
|
|
Region faces = cuboid.getFaces();
|
|
|
|
return setBlocks(faces, pattern);
|
2011-08-22 06:55:50 +00:00
|
|
|
}
|
2010-10-11 08:22:47 +00:00
|
|
|
|
2013-11-01 18:05:49 +00:00
|
|
|
/**
|
2014-03-28 07:40:04 +00:00
|
|
|
* Make the faces of the given region. The method by which the faces are found
|
|
|
|
* may be inefficient, because there may not be an efficient implementation supported
|
|
|
|
* for that specific shape.
|
2013-11-01 18:05:49 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param region the region
|
2014-03-28 07:40:04 +00:00
|
|
|
* @param pattern the pattern to place
|
2013-11-01 18:05:49 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-28 07:40:04 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2013-11-01 18:05:49 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int makeFaces(final Region region, Pattern pattern) throws MaxChangedBlocksException {
|
2014-03-28 07:40:04 +00:00
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(pattern);
|
|
|
|
|
|
|
|
if (region instanceof CuboidRegion) {
|
2019-06-04 15:48:30 +00:00
|
|
|
return makeCuboidFaces(region, pattern);
|
2014-03-28 07:40:04 +00:00
|
|
|
} else {
|
|
|
|
return new RegionShape(region).generate(this, pattern, true);
|
|
|
|
}
|
2013-11-01 18:05:49 +00:00
|
|
|
}
|
|
|
|
|
2018-12-20 16:00:27 +00:00
|
|
|
|
2010-10-18 17:45:03 +00:00
|
|
|
/**
|
2014-03-28 07:40:04 +00:00
|
|
|
* Make the walls (all faces but those parallel to the X-Z plane) of the given region
|
|
|
|
* as if it was a {@link CuboidRegion}.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-28 07:40:04 +00:00
|
|
|
* @param region the region
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param block the block to place
|
2010-10-18 17:45:03 +00:00
|
|
|
* @return number of blocks affected
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-18 17:45:03 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public <B extends BlockStateHolder<B>> int makeCuboidWalls(Region region, B block) throws MaxChangedBlocksException {
|
2019-09-14 03:05:16 +00:00
|
|
|
return makeCuboidWalls(region, (Pattern) block);
|
2010-10-18 17:45:03 +00:00
|
|
|
}
|
|
|
|
|
2011-08-22 06:55:50 +00:00
|
|
|
/**
|
2014-03-28 07:40:04 +00:00
|
|
|
* Make the walls (all faces but those parallel to the X-Z plane) of the given region
|
|
|
|
* as if it was a {@link CuboidRegion}.
|
2011-08-22 06:55:50 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param region the region
|
2014-03-28 07:40:04 +00:00
|
|
|
* @param pattern the pattern to place
|
2011-08-22 06:55:50 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-28 07:40:04 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2011-08-22 06:55:50 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int makeCuboidWalls(Region region, Pattern pattern) throws MaxChangedBlocksException {
|
2014-03-28 07:40:04 +00:00
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(pattern);
|
2011-08-22 06:55:50 +00:00
|
|
|
|
2020-02-29 23:52:50 +00:00
|
|
|
int blocksChanged = 0;
|
|
|
|
for (Region wall : CuboidRegion.makeCuboid(region).getWalls().getRegions()) {
|
|
|
|
blocksChanged += setBlocks(wall, pattern);
|
|
|
|
}
|
|
|
|
return blocksChanged;
|
2011-08-22 06:55:50 +00:00
|
|
|
}
|
|
|
|
|
2013-11-01 18:05:49 +00:00
|
|
|
/**
|
2014-03-28 07:40:04 +00:00
|
|
|
* Make the walls of the given region. The method by which the walls are found
|
|
|
|
* may be inefficient, because there may not be an efficient implementation supported
|
|
|
|
* for that specific shape.
|
2013-11-01 18:05:49 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param region the region
|
2014-03-28 07:40:04 +00:00
|
|
|
* @param pattern the pattern to place
|
2013-11-01 18:05:49 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-28 07:40:04 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2013-11-01 18:05:49 +00:00
|
|
|
*/
|
2019-06-25 13:00:44 +00:00
|
|
|
public int makeWalls(final Region region, Pattern pattern) throws MaxChangedBlocksException {
|
2014-03-28 07:40:04 +00:00
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(pattern);
|
2013-11-03 09:15:28 +00:00
|
|
|
|
2014-03-28 07:40:04 +00:00
|
|
|
if (region instanceof CuboidRegion) {
|
2019-06-04 15:48:30 +00:00
|
|
|
return makeCuboidWalls(region, pattern);
|
2014-03-28 07:40:04 +00:00
|
|
|
} else {
|
2020-01-04 10:11:13 +00:00
|
|
|
replaceBlocks(region, new Mask() {
|
|
|
|
@Override
|
|
|
|
public boolean test(Extent extent, BlockVector3 position) {
|
|
|
|
int x = position.getBlockX();
|
|
|
|
int z = position.getBlockZ();
|
2020-04-23 16:07:01 +00:00
|
|
|
return !region.contains(x, z + 1) || !region.contains(x, z - 1) || !region
|
|
|
|
.contains(x + 1, z) || !region.contains(x - 1, z);
|
2020-01-04 10:11:13 +00:00
|
|
|
}
|
2019-07-18 06:56:57 +00:00
|
|
|
}, pattern);
|
2014-03-28 07:40:04 +00:00
|
|
|
}
|
2018-09-13 16:42:55 +00:00
|
|
|
return changes;
|
2013-11-01 18:05:49 +00:00
|
|
|
}
|
|
|
|
|
2010-10-11 08:22:47 +00:00
|
|
|
/**
|
2014-03-28 23:49:01 +00:00
|
|
|
* Places a layer of blocks on top of ground blocks in the given region
|
|
|
|
* (as if it were a cuboid).
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-28 23:49:01 +00:00
|
|
|
* @param region the region
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param block the placed block
|
2010-10-11 15:56:19 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-28 23:49:01 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-11 08:22:47 +00:00
|
|
|
*/
|
2018-12-27 00:39:10 +00:00
|
|
|
public <B extends BlockStateHolder<B>> int overlayCuboidBlocks(Region region, B block) throws MaxChangedBlocksException {
|
2014-03-28 23:49:01 +00:00
|
|
|
checkNotNull(block);
|
2019-09-14 03:05:16 +00:00
|
|
|
|
|
|
|
return overlayCuboidBlocks(region, (Pattern) block);
|
2011-01-30 08:47:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-03-28 23:49:01 +00:00
|
|
|
* Places a layer of blocks on top of ground blocks in the given region
|
|
|
|
* (as if it were a cuboid).
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param region the region
|
2014-03-28 23:49:01 +00:00
|
|
|
* @param pattern the placed block pattern
|
2011-01-30 08:47:02 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-28 23:49:01 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2011-01-30 08:47:02 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int overlayCuboidBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException {
|
2014-03-28 23:49:01 +00:00
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(pattern);
|
2019-06-25 13:00:44 +00:00
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
BlockReplace replace = new BlockReplace(this, pattern);
|
|
|
|
RegionOffset offset = new RegionOffset(BlockVector3.UNIT_Y, replace);
|
2018-08-12 14:03:07 +00:00
|
|
|
int minY = region.getMinimumPoint().getBlockY();
|
|
|
|
int maxY = Math.min(getMaximumPoint().getBlockY(), region.getMaximumPoint().getBlockY() + 1);
|
|
|
|
SurfaceRegionFunction surface = new SurfaceRegionFunction(this, offset, minY, maxY);
|
2019-07-18 06:56:57 +00:00
|
|
|
FlatRegionVisitor visitor = new FlatRegionVisitor(asFlatRegion(region), surface);
|
2018-08-12 14:03:07 +00:00
|
|
|
Operations.completeBlindly(visitor);
|
|
|
|
return this.changes = visitor.getAffected();
|
2010-10-11 08:22:47 +00:00
|
|
|
}
|
|
|
|
|
2011-08-02 22:46:11 +00:00
|
|
|
/**
|
|
|
|
* Turns the first 3 layers into dirt/grass and the bottom layers
|
|
|
|
* into rock, like a natural Minecraft mountain.
|
|
|
|
*
|
2014-03-30 01:32:10 +00:00
|
|
|
* @param region the region to affect
|
2011-08-02 22:46:11 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-30 01:32:10 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2011-08-02 22:46:11 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int naturalizeCuboidBlocks(Region region) throws MaxChangedBlocksException {
|
2014-03-30 01:32:10 +00:00
|
|
|
checkNotNull(region);
|
2019-06-04 15:48:30 +00:00
|
|
|
|
|
|
|
Naturalizer naturalizer = new Naturalizer(this);
|
|
|
|
FlatRegion flatRegion = Regions.asFlatRegion(region);
|
|
|
|
LayerVisitor visitor = new LayerVisitor(flatRegion, minimumBlockY(region), maximumBlockY(region), naturalizer);
|
2018-08-12 14:03:07 +00:00
|
|
|
Operations.completeBlindly(visitor);
|
|
|
|
return this.changes = naturalizer.getAffected();
|
|
|
|
}
|
2011-08-02 22:46:11 +00:00
|
|
|
|
2010-10-11 17:27:18 +00:00
|
|
|
/**
|
2019-11-20 00:11:54 +00:00
|
|
|
* Stack a cuboid region. For compatibility, entities are copied by biomes are not.
|
|
|
|
* Use {@link #stackCuboidRegion(Region, BlockVector3, int, boolean, boolean, Mask)} to fine tune.
|
|
|
|
*
|
|
|
|
* @param region the region to stack
|
|
|
|
* @param dir the direction to stack
|
|
|
|
* @param count the number of times to stack
|
|
|
|
* @param copyAir true to also copy air blocks
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
public int stackCuboidRegion(Region region, BlockVector3 dir, int count, boolean copyAir) throws MaxChangedBlocksException {
|
|
|
|
return stackCuboidRegion(region, dir, count, true, false, copyAir ? null : new ExistingBlockMask(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stack a cuboid region.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param region the region to stack
|
|
|
|
* @param dir the direction to stack
|
|
|
|
* @param count the number of times to stack
|
2019-08-03 16:59:24 +00:00
|
|
|
* @param copyEntities true to copy entities
|
|
|
|
* @param copyBiomes true to copy biomes
|
|
|
|
* @param mask source mask for the operation (only matching blocks are copied)
|
2010-10-11 17:27:18 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-30 10:08:56 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-11 17:27:18 +00:00
|
|
|
*/
|
2019-11-20 00:11:54 +00:00
|
|
|
public int stackCuboidRegion(Region region, BlockVector3 dir, int count,
|
|
|
|
boolean copyEntities, boolean copyBiomes, Mask mask) throws MaxChangedBlocksException {
|
2014-03-30 10:08:56 +00:00
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(dir);
|
|
|
|
checkArgument(count >= 1, "count >= 1 required");
|
2019-06-25 13:00:44 +00:00
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1);
|
|
|
|
BlockVector3 to = region.getMinimumPoint();
|
|
|
|
ForwardExtentCopy copy = new ForwardExtentCopy(this, region, this, to);
|
2014-03-30 10:08:56 +00:00
|
|
|
copy.setRepetitions(count);
|
|
|
|
copy.setTransform(new AffineTransform().translate(dir.multiply(size)));
|
2020-03-21 01:39:40 +00:00
|
|
|
copy.setCopyingEntities(copyEntities);
|
|
|
|
copy.setCopyingBiomes(copyBiomes);
|
2019-11-20 00:11:54 +00:00
|
|
|
mask = MaskIntersection.of(getSourceMask(), mask).optimize();
|
|
|
|
if (mask != Masks.alwaysTrue()) {
|
2018-08-12 14:03:07 +00:00
|
|
|
setSourceMask(null);
|
2019-11-20 00:11:54 +00:00
|
|
|
copy.setSourceMask(mask);
|
2010-10-11 08:22:47 +00:00
|
|
|
}
|
2020-04-10 17:22:28 +00:00
|
|
|
Operations.completeLegacy(copy);
|
2018-08-12 14:03:07 +00:00
|
|
|
return this.changes = copy.getAffected();
|
2010-10-11 08:22:47 +00:00
|
|
|
}
|
2010-10-11 18:17:32 +00:00
|
|
|
|
2020-03-21 01:39:40 +00:00
|
|
|
/**
|
|
|
|
* 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 copyAir true to copy air blocks
|
|
|
|
* @param moveEntities true to move entities
|
|
|
|
* @param copyBiomes true to copy biomes
|
|
|
|
* @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, boolean moveEntities, boolean copyBiomes, Pattern replacement) throws MaxChangedBlocksException {
|
2019-11-19 22:07:30 +00:00
|
|
|
Mask mask = null;
|
|
|
|
if (!copyAir) {
|
|
|
|
mask = new ExistingBlockMask(this);
|
|
|
|
}
|
|
|
|
return moveRegion(region, dir, distance, moveEntities, copyBiomes, mask, replacement);
|
|
|
|
}
|
|
|
|
|
2013-08-03 23:22:29 +00:00
|
|
|
/**
|
2014-03-30 17:29:00 +00:00
|
|
|
* Move the blocks in a region a certain direction.
|
2013-08-03 23:22:29 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param region the region to move
|
|
|
|
* @param dir the direction
|
|
|
|
* @param distance the distance to move
|
2019-08-03 16:59:24 +00:00
|
|
|
* @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)
|
2018-12-22 07:26:02 +00:00
|
|
|
* @param replacement the replacement pattern to fill in after moving, or null to use air
|
2013-08-03 23:22:29 +00:00
|
|
|
* @return number of blocks moved
|
2014-03-30 17:29:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2019-08-03 16:59:24 +00:00
|
|
|
* @throws IllegalArgumentException thrown if the region is not a flat region, but copyBiomes is true
|
2013-08-03 23:22:29 +00:00
|
|
|
*/
|
2019-11-19 22:07:30 +00:00
|
|
|
public int moveRegion(Region region, BlockVector3 dir, int distance,
|
|
|
|
boolean moveEntities, boolean copyBiomes, Mask mask, Pattern replacement) throws MaxChangedBlocksException {
|
2014-03-30 17:29:00 +00:00
|
|
|
checkNotNull(region);
|
|
|
|
checkNotNull(dir);
|
|
|
|
checkArgument(distance >= 1, "distance >= 1 required");
|
2019-09-14 03:05:16 +00:00
|
|
|
checkArgument(!copyBiomes || region instanceof FlatRegion, "can't copy biomes from non-flat region");
|
|
|
|
|
2019-06-25 13:00:44 +00:00
|
|
|
BlockVector3 to = region.getMinimumPoint().add(dir.multiply(distance));
|
2013-08-03 23:22:29 +00:00
|
|
|
|
2019-06-25 13:00:44 +00:00
|
|
|
final BlockVector3 displace = dir.multiply(distance);
|
2018-12-23 16:19:33 +00:00
|
|
|
final BlockVector3 size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1);
|
2013-08-03 23:22:29 +00:00
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
BlockVector3 disAbs = displace.abs();
|
2014-03-30 17:29:00 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
if (disAbs.getBlockX() < size.getBlockX() && disAbs.getBlockY() < size.getBlockY() && disAbs.getBlockZ() < size.getBlockZ()) {
|
|
|
|
// Buffer if overlapping
|
2019-07-18 16:07:31 +00:00
|
|
|
disableQueue();
|
2013-08-03 23:22:29 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
ForwardExtentCopy copy = new ForwardExtentCopy(this, region, this, to);
|
2014-03-30 17:29:00 +00:00
|
|
|
|
2019-06-23 18:05:14 +00:00
|
|
|
if (replacement == null) replacement = BlockTypes.AIR.getDefaultState();
|
2019-07-06 00:46:48 +00:00
|
|
|
BlockReplace remove = replacement instanceof ExistingPattern ? null : new BlockReplace(this, replacement);
|
2019-06-25 13:00:44 +00:00
|
|
|
copy.setSourceFunction(remove); // Remove
|
2019-09-14 03:05:16 +00:00
|
|
|
|
|
|
|
copy.setCopyingEntities(moveEntities);
|
|
|
|
copy.setRemovingEntities(moveEntities);
|
|
|
|
copy.setCopyingBiomes(copyBiomes);
|
2018-08-12 14:03:07 +00:00
|
|
|
copy.setRepetitions(1);
|
2019-11-19 22:07:30 +00:00
|
|
|
if (mask != null) {
|
|
|
|
new MaskTraverser(mask).reset(this);
|
|
|
|
copy.setSourceMask(mask);
|
|
|
|
if (this.getSourceMask() == mask) {
|
|
|
|
setSourceMask(null);
|
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
2019-09-14 03:05:16 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
Operations.completeBlindly(copy);
|
|
|
|
return this.changes = copy.getAffected();
|
2013-08-03 23:22:29 +00:00
|
|
|
}
|
|
|
|
|
2010-10-18 20:51:43 +00:00
|
|
|
/**
|
2014-03-30 20:56:12 +00:00
|
|
|
* Move the blocks in a region a certain direction.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param region the region to move
|
|
|
|
* @param dir the direction
|
|
|
|
* @param distance the distance to move
|
|
|
|
* @param copyAir true to copy air blocks
|
2018-12-22 07:26:02 +00:00
|
|
|
* @param replacement the replacement pattern to fill in after moving, or null to use air
|
2010-10-18 20:51:43 +00:00
|
|
|
* @return number of blocks moved
|
2014-03-30 20:56:12 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-18 20:51:43 +00:00
|
|
|
*/
|
2019-07-06 00:46:48 +00:00
|
|
|
public int moveCuboidRegion(Region region, BlockVector3 dir, int distance, boolean copyAir, Pattern replacement) throws MaxChangedBlocksException {
|
2019-06-04 15:48:30 +00:00
|
|
|
return moveRegion(region, dir, distance, copyAir, true, false, replacement);
|
2010-10-18 20:51:43 +00:00
|
|
|
}
|
|
|
|
|
2010-10-11 18:17:32 +00:00
|
|
|
/**
|
|
|
|
* Drain nearby pools of water or lava.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-03-30 21:10:01 +00:00
|
|
|
* @param origin the origin to drain from, which will search a 3x3 area
|
|
|
|
* @param radius the radius of the removal, where a value should be 0 or greater
|
2010-10-11 18:17:32 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-30 21:46:50 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-11 18:17:32 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int drainArea(BlockVector3 origin, double radius) throws MaxChangedBlocksException {
|
2019-02-12 21:39:09 +00:00
|
|
|
return drainArea(origin, radius, false);
|
|
|
|
}
|
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
/**
|
|
|
|
* Drain nearby pools of water or lava, optionally removed waterlogged states from blocks.
|
|
|
|
*
|
2019-02-12 21:39:09 +00:00
|
|
|
* @param origin the origin to drain from, which will search a 3x3 area
|
2019-06-04 15:48:30 +00:00
|
|
|
* @param radius the radius of the removal, where a value should be 0 or greater
|
|
|
|
* @param waterlogged true to make waterlogged blocks non-waterlogged as well
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
public int drainArea(BlockVector3 origin, double radius, boolean waterlogged) throws MaxChangedBlocksException {
|
2014-03-30 21:10:01 +00:00
|
|
|
checkNotNull(origin);
|
|
|
|
checkArgument(radius >= 0, "radius >= 0 required");
|
2019-06-25 13:00:44 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
Mask liquidMask;
|
|
|
|
// Not thread safe, use hardcoded liquidmask
|
|
|
|
// if (getWorld() != null) {
|
|
|
|
// liquidMask = getWorld().createLiquidMask();
|
|
|
|
// } else {
|
|
|
|
// }
|
2019-06-12 21:12:12 +00:00
|
|
|
liquidMask = new BlockTypeMask(this, BlockTypes.LAVA, BlockTypes.WATER);
|
2020-01-04 10:11:13 +00:00
|
|
|
Mask mask = new MaskIntersection(
|
2019-06-12 21:12:12 +00:00
|
|
|
new BoundedHeightMask(0, getWorld().getMaxY()),
|
2019-06-25 13:00:44 +00:00
|
|
|
new RegionMask(new EllipsoidRegion(null, origin, Vector3.at(radius, radius, radius))),
|
2020-01-04 10:11:13 +00:00
|
|
|
liquidMask).withExtent(getExtent());
|
2019-06-12 21:12:12 +00:00
|
|
|
BlockReplace replace;
|
|
|
|
if (waterlogged) {
|
|
|
|
replace = new BlockReplace(this, new WaterloggedRemover(this));
|
|
|
|
} else {
|
2019-07-06 00:46:48 +00:00
|
|
|
replace = new BlockReplace(this, BlockTypes.AIR.getDefaultState());
|
2019-06-12 21:12:12 +00:00
|
|
|
}
|
2019-07-18 06:56:57 +00:00
|
|
|
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1));
|
2014-03-30 21:10:01 +00:00
|
|
|
|
|
|
|
// Around the origin in a 3x3 block
|
2019-06-04 15:48:30 +00:00
|
|
|
for (BlockVector3 position : CuboidRegion.fromCenter(origin, 1)) {
|
2020-01-04 10:11:13 +00:00
|
|
|
if (mask.test(getExtent(), position)) {
|
2014-03-30 21:10:01 +00:00
|
|
|
visitor.visit(position);
|
2010-10-11 18:17:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-01 23:21:27 +00:00
|
|
|
Operations.completeLegacy(visitor);
|
2019-06-25 13:00:44 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
return this.changes = visitor.getAffected();
|
2010-10-15 08:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-03-30 21:46:50 +00:00
|
|
|
* Fix liquids so that they turn into stationary blocks and extend outward.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param origin the original position
|
|
|
|
* @param radius the radius to fix
|
2019-06-04 15:48:30 +00:00
|
|
|
* @param fluid the type of the fluid
|
2010-10-15 08:07:48 +00:00
|
|
|
* @return number of blocks affected
|
2014-03-30 21:46:50 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-15 08:07:48 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int fixLiquid(BlockVector3 origin, double radius, BlockType fluid) throws MaxChangedBlocksException {
|
2014-03-30 21:45:42 +00:00
|
|
|
checkNotNull(origin);
|
|
|
|
checkArgument(radius >= 0, "radius >= 0 required");
|
2010-10-15 08:07:48 +00:00
|
|
|
|
2019-04-09 03:29:43 +00:00
|
|
|
// Our origins can only be liquids
|
2019-11-14 17:57:43 +00:00
|
|
|
Mask liquidMask = new SingleBlockTypeMask(this, fluid);
|
2019-04-09 03:29:43 +00:00
|
|
|
|
|
|
|
// But we will also visit air blocks
|
|
|
|
MaskIntersection blockMask = new MaskUnion(liquidMask, Masks.negate(new ExistingBlockMask(this)));
|
|
|
|
|
2014-03-30 21:45:42 +00:00
|
|
|
// There are boundaries that the routine needs to stay in
|
2020-02-06 00:43:50 +00:00
|
|
|
Mask mask = new MaskIntersection(
|
2019-04-09 03:29:43 +00:00
|
|
|
new BoundedHeightMask(0, Math.min(origin.getBlockY(), getWorld().getMaxY())),
|
2018-10-19 20:13:32 +00:00
|
|
|
new RegionMask(new EllipsoidRegion(null, origin, Vector3.at(radius, radius, radius))),
|
2019-06-04 15:48:30 +00:00
|
|
|
blockMask
|
2020-01-04 10:11:13 +00:00
|
|
|
).withExtent(getExtent());
|
2019-07-06 00:46:48 +00:00
|
|
|
BlockReplace replace = new BlockReplace(this, fluid.getDefaultState());
|
2019-04-09 03:29:43 +00:00
|
|
|
NonRisingVisitor visitor = new NonRisingVisitor(mask, replace);
|
2011-09-24 19:32:03 +00:00
|
|
|
|
2019-11-21 07:57:32 +00:00
|
|
|
// Around the origin in a 3x3 block
|
2018-12-23 16:19:33 +00:00
|
|
|
for (BlockVector3 position : CuboidRegion.fromCenter(origin, 1)) {
|
2020-01-04 10:11:13 +00:00
|
|
|
if (liquidMask.test(getExtent(), position)) {
|
2014-03-30 21:45:42 +00:00
|
|
|
visitor.visit(position);
|
2010-10-15 08:07:48 +00:00
|
|
|
}
|
|
|
|
}
|
2010-10-11 18:17:32 +00:00
|
|
|
|
2019-04-09 03:29:43 +00:00
|
|
|
Operations.completeLegacy(visitor);
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-04-09 03:29:43 +00:00
|
|
|
return visitor.getAffected();
|
2010-10-11 18:17:32 +00:00
|
|
|
}
|
2010-10-18 00:22:29 +00:00
|
|
|
|
|
|
|
/**
|
2011-10-26 20:50:46 +00:00
|
|
|
* Makes a cylinder.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param pos Center of the cylinder
|
|
|
|
* @param block The block pattern to use
|
2011-10-26 20:50:46 +00:00
|
|
|
* @param radius The cylinder's radius
|
|
|
|
* @param height The cylinder's up/down extent. If negative, extend downward.
|
|
|
|
* @param filled If false, only a shell will be generated.
|
|
|
|
* @return number of blocks changed
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2011-10-26 20:50:46 +00:00
|
|
|
*/
|
2019-03-28 19:02:37 +00:00
|
|
|
public int makeCylinder(BlockVector3 pos, Pattern block, double radius, int height, boolean filled) throws MaxChangedBlocksException {
|
|
|
|
return makeCylinder(pos, block, radius, radius, height, filled);
|
2011-10-26 20:50:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Makes a cylinder.
|
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param pos Center of the cylinder
|
|
|
|
* @param block The block pattern to use
|
2011-10-28 18:16:19 +00:00
|
|
|
* @param radiusX The cylinder's largest north/south extent
|
|
|
|
* @param radiusZ The cylinder's largest east/west extent
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param height The cylinder's up/down extent. If negative, extend downward.
|
|
|
|
* @param filled If false, only a shell will be generated.
|
2011-10-28 18:16:19 +00:00
|
|
|
* @return number of blocks changed
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-18 00:22:29 +00:00
|
|
|
*/
|
2019-07-06 00:46:48 +00:00
|
|
|
public int makeCylinder(BlockVector3 pos, Pattern block, double radiusX, double radiusZ, int height, boolean filled) throws MaxChangedBlocksException {
|
2018-08-12 14:03:07 +00:00
|
|
|
return makeCylinder(pos, block, radiusX, radiusZ, height, 0, filled);
|
|
|
|
}
|
2011-01-01 18:33:18 +00:00
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
public int makeHollowCylinder(BlockVector3 pos, final Pattern block, double radiusX, double radiusZ, int height, double thickness) throws MaxChangedBlocksException {
|
2018-08-12 14:03:07 +00:00
|
|
|
return makeCylinder(pos, block, radiusX, radiusZ, height, thickness, false);
|
|
|
|
}
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
private int makeCylinder(BlockVector3 pos, Pattern block, double radiusX, double radiusZ, int height, double thickness, boolean filled) throws MaxChangedBlocksException {
|
2011-10-28 18:16:19 +00:00
|
|
|
radiusX += 0.5;
|
|
|
|
radiusZ += 0.5;
|
2019-06-25 13:00:44 +00:00
|
|
|
|
2019-03-31 16:09:20 +00:00
|
|
|
MutableBlockVector3 posv = new MutableBlockVector3(pos);
|
2010-10-18 00:39:20 +00:00
|
|
|
if (height == 0) {
|
2019-06-25 13:00:44 +00:00
|
|
|
return 0;
|
2010-10-18 00:39:20 +00:00
|
|
|
} else if (height < 0) {
|
|
|
|
height = -height;
|
2018-12-23 16:19:33 +00:00
|
|
|
posv.mutY(posv.getY() - height);
|
2010-10-18 00:39:20 +00:00
|
|
|
}
|
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
if (posv.getBlockY() < 0) {
|
|
|
|
posv.mutY(0);
|
2019-07-06 00:46:48 +00:00
|
|
|
} else if (posv.getBlockY() + height - 1 > maxY) {
|
|
|
|
height = maxY - posv.getBlockY() + 1;
|
2010-10-18 00:39:20 +00:00
|
|
|
}
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
final double invRadiusX = 1 / radiusX;
|
|
|
|
final double invRadiusZ = 1 / radiusZ;
|
2018-08-12 14:03:07 +00:00
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
int px = posv.getBlockX();
|
|
|
|
int py = posv.getBlockY();
|
|
|
|
int pz = posv.getBlockZ();
|
2019-03-31 16:09:20 +00:00
|
|
|
MutableBlockVector3 mutable = new MutableBlockVector3();
|
2018-08-12 14:03:07 +00:00
|
|
|
|
|
|
|
final int ceilRadiusX = (int) Math.ceil(radiusX);
|
|
|
|
final int ceilRadiusZ = (int) Math.ceil(radiusZ);
|
2019-09-14 03:05:16 +00:00
|
|
|
|
2019-07-17 16:31:13 +00:00
|
|
|
double xSqr, zSqr;
|
2019-07-06 00:46:48 +00:00
|
|
|
double distanceSq;
|
2018-08-12 14:03:07 +00:00
|
|
|
double nextXn = 0;
|
|
|
|
|
|
|
|
if (thickness != 0) {
|
|
|
|
double nextMinXn = 0;
|
|
|
|
final double minInvRadiusX = 1 / (radiusX - thickness);
|
|
|
|
final double minInvRadiusZ = 1 / (radiusZ - thickness);
|
2019-06-25 13:00:44 +00:00
|
|
|
forX: for (int x = 0; x <= ceilRadiusX; ++x) {
|
2018-08-12 14:03:07 +00:00
|
|
|
final double xn = nextXn;
|
|
|
|
double dx2 = nextMinXn * nextMinXn;
|
|
|
|
nextXn = (x + 1) * invRadiusX;
|
|
|
|
nextMinXn = (x + 1) * minInvRadiusX;
|
|
|
|
double nextZn = 0;
|
|
|
|
double nextMinZn = 0;
|
2019-07-17 16:31:13 +00:00
|
|
|
xSqr = xn * xn;
|
2019-06-25 13:00:44 +00:00
|
|
|
forZ: for (int z = 0; z <= ceilRadiusZ; ++z) {
|
2018-08-12 14:03:07 +00:00
|
|
|
final double zn = nextZn;
|
|
|
|
double dz2 = nextMinZn * nextMinZn;
|
|
|
|
nextZn = (z + 1) * invRadiusZ;
|
|
|
|
nextMinZn = (z + 1) * minInvRadiusZ;
|
2019-07-17 16:31:13 +00:00
|
|
|
zSqr = zn * zn;
|
|
|
|
distanceSq = xSqr + zSqr;
|
2019-07-06 00:46:48 +00:00
|
|
|
if (distanceSq > 1) {
|
2018-08-12 14:03:07 +00:00
|
|
|
if (z == 0) {
|
|
|
|
break forX;
|
|
|
|
}
|
|
|
|
break forZ;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((dz2 + nextMinXn * nextMinXn <= 1) && (nextMinZn * nextMinZn + dx2 <= 1)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int y = 0; y < height; ++y) {
|
2019-03-31 16:09:20 +00:00
|
|
|
this.setBlock(mutable.setComponents(px + x, py + y, pz + z), block);
|
|
|
|
this.setBlock(mutable.setComponents(px - x, py + y, pz + z), block);
|
|
|
|
this.setBlock(mutable.setComponents(px + x, py + y, pz - z), block);
|
|
|
|
this.setBlock(mutable.setComponents(px - x, py + y, pz - z), block);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2019-06-25 13:00:44 +00:00
|
|
|
forX: for (int x = 0; x <= ceilRadiusX; ++x) {
|
2018-08-12 14:03:07 +00:00
|
|
|
final double xn = nextXn;
|
|
|
|
nextXn = (x + 1) * invRadiusX;
|
|
|
|
double nextZn = 0;
|
2019-07-17 16:31:13 +00:00
|
|
|
xSqr = xn * xn;
|
2019-06-25 13:00:44 +00:00
|
|
|
forZ: for (int z = 0; z <= ceilRadiusZ; ++z) {
|
2018-08-12 14:03:07 +00:00
|
|
|
final double zn = nextZn;
|
|
|
|
nextZn = (z + 1) * invRadiusZ;
|
2019-07-17 16:31:13 +00:00
|
|
|
zSqr = zn * zn;
|
|
|
|
distanceSq = xSqr + zSqr;
|
2019-07-06 00:46:48 +00:00
|
|
|
if (distanceSq > 1) {
|
2018-08-12 14:03:07 +00:00
|
|
|
if (z == 0) {
|
|
|
|
break forX;
|
|
|
|
}
|
|
|
|
break forZ;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!filled) {
|
2019-07-17 16:31:13 +00:00
|
|
|
if ((zSqr + nextXn * nextXn <= 1) && (nextZn * nextZn + xSqr <= 1)) {
|
2018-08-12 14:03:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int y = 0; y < height; ++y) {
|
2019-03-31 16:09:20 +00:00
|
|
|
this.setBlock(mutable.setComponents(px + x, py + y, pz + z), block);
|
|
|
|
this.setBlock(mutable.setComponents(px - x, py + y, pz + z), block);
|
|
|
|
this.setBlock(mutable.setComponents(px + x, py + y, pz - z), block);
|
|
|
|
this.setBlock(mutable.setComponents(px - x, py + y, pz - z), block);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.changes;
|
|
|
|
}
|
|
|
|
|
2010-10-18 00:22:29 +00:00
|
|
|
/**
|
2019-11-19 21:23:47 +00:00
|
|
|
* Move the blocks in a region a certain direction.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2019-11-19 21:23:47 +00:00
|
|
|
* @param region the region to move
|
|
|
|
* @param dir the direction
|
|
|
|
* @param distance the distance to move
|
|
|
|
* @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
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2011-10-26 20:50:46 +00:00
|
|
|
*/
|
2019-11-19 21:23:47 +00:00
|
|
|
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);
|
2011-10-26 20:50:46 +00:00
|
|
|
}
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
public int makeCircle(BlockVector3 pos, final Pattern block, double radiusX, double radiusY, double radiusZ, boolean filled, Vector3 normal) throws MaxChangedBlocksException {
|
2018-08-12 14:03:07 +00:00
|
|
|
radiusX += 0.5;
|
|
|
|
radiusY += 0.5;
|
|
|
|
radiusZ += 0.5;
|
|
|
|
|
|
|
|
normal = normal.normalize();
|
|
|
|
double nx = normal.getX();
|
|
|
|
double ny = normal.getY();
|
|
|
|
double nz = normal.getZ();
|
|
|
|
|
|
|
|
|
2011-10-28 18:16:19 +00:00
|
|
|
final double invRadiusX = 1 / radiusX;
|
2018-08-12 14:03:07 +00:00
|
|
|
final double invRadiusY = 1 / radiusY;
|
2011-10-28 18:16:19 +00:00
|
|
|
final double invRadiusZ = 1 / radiusZ;
|
2010-10-18 00:22:29 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
int px = pos.getBlockX();
|
|
|
|
int py = pos.getBlockY();
|
|
|
|
int pz = pos.getBlockZ();
|
2019-03-31 16:09:20 +00:00
|
|
|
MutableBlockVector3 mutable = new MutableBlockVector3();
|
2018-08-12 14:03:07 +00:00
|
|
|
|
2011-10-28 18:16:19 +00:00
|
|
|
final int ceilRadiusX = (int) Math.ceil(radiusX);
|
2018-08-12 14:03:07 +00:00
|
|
|
final int ceilRadiusY = (int) Math.ceil(radiusY);
|
2011-10-28 18:16:19 +00:00
|
|
|
final int ceilRadiusZ = (int) Math.ceil(radiusZ);
|
2010-10-18 00:22:29 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
double threshold = 0.5;
|
|
|
|
|
|
|
|
LocalBlockVectorSet set = new LocalBlockVectorSet();
|
|
|
|
|
2011-10-28 18:16:19 +00:00
|
|
|
double nextXn = 0;
|
2018-08-12 14:03:07 +00:00
|
|
|
double dx, dy, dz, dxy, dxyz;
|
|
|
|
forX:
|
|
|
|
for (int x = 0; x <= ceilRadiusX; ++x) {
|
2011-10-28 18:16:19 +00:00
|
|
|
final double xn = nextXn;
|
2018-08-12 14:03:07 +00:00
|
|
|
dx = xn * xn;
|
2011-10-28 18:16:19 +00:00
|
|
|
nextXn = (x + 1) * invRadiusX;
|
2018-08-12 14:03:07 +00:00
|
|
|
double nextYn = 0;
|
|
|
|
forY:
|
|
|
|
for (int y = 0; y <= ceilRadiusY; ++y) {
|
|
|
|
final double yn = nextYn;
|
|
|
|
dy = yn * yn;
|
|
|
|
dxy = dx + dy;
|
|
|
|
nextYn = (y + 1) * invRadiusY;
|
|
|
|
double nextZn = 0;
|
|
|
|
forZ:
|
|
|
|
for (int z = 0; z <= ceilRadiusZ; ++z) {
|
|
|
|
final double zn = nextZn;
|
|
|
|
dz = zn * zn;
|
|
|
|
dxyz = dxy + dz;
|
|
|
|
nextZn = (z + 1) * invRadiusZ;
|
|
|
|
if (dxyz > 1) {
|
|
|
|
if (z == 0) {
|
|
|
|
if (y == 0) {
|
|
|
|
break forX;
|
|
|
|
}
|
|
|
|
break forY;
|
|
|
|
}
|
|
|
|
break forZ;
|
2011-10-28 18:16:19 +00:00
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
if (!filled) {
|
|
|
|
if (nextXn * nextXn + dy + dz <= 1 && nextYn * nextYn + dx + dz <= 1 && nextZn * nextZn + dx + dy <= 1) {
|
|
|
|
continue;
|
|
|
|
}
|
2011-10-28 18:16:19 +00:00
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
if (Math.abs((x) * nx + (y) * ny + (z) * nz) < threshold)
|
2019-03-31 16:09:20 +00:00
|
|
|
setBlock(mutable.setComponents(px + x, py + y, pz + z), block);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (Math.abs((-x) * nx + (y) * ny + (z) * nz) < threshold)
|
2019-03-31 16:09:20 +00:00
|
|
|
setBlock(mutable.setComponents(px - x, py + y, pz + z), block);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (Math.abs((x) * nx + (-y) * ny + (z) * nz) < threshold)
|
2019-03-31 16:09:20 +00:00
|
|
|
setBlock(mutable.setComponents(px + x, py - y, pz + z), block);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (Math.abs((x) * nx + (y) * ny + (-z) * nz) < threshold)
|
2019-03-31 16:09:20 +00:00
|
|
|
setBlock(mutable.setComponents(px + x, py + y, pz - z), block);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (Math.abs((-x) * nx + (-y) * ny + (z) * nz) < threshold)
|
2019-03-31 16:09:20 +00:00
|
|
|
setBlock(mutable.setComponents(px - x, py - y, pz + z), block);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (Math.abs((x) * nx + (-y) * ny + (-z) * nz) < threshold)
|
2019-03-31 16:09:20 +00:00
|
|
|
setBlock(mutable.setComponents(px + x, py - y, pz - z), block);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (Math.abs((-x) * nx + (y) * ny + (-z) * nz) < threshold)
|
2019-03-31 16:09:20 +00:00
|
|
|
setBlock(mutable.setComponents(px - x, py + y, pz - z), block);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (Math.abs((-x) * nx + (-y) * ny + (-z) * nz) < threshold)
|
2019-03-31 16:09:20 +00:00
|
|
|
setBlock(mutable.setComponents(px - x, py - y, pz - z), block);
|
2010-10-18 00:22:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
return changes;
|
2010-10-18 00:22:29 +00:00
|
|
|
}
|
|
|
|
|
2011-10-26 20:50:46 +00:00
|
|
|
/**
|
2018-12-20 16:00:27 +00:00
|
|
|
* Makes a sphere.
|
|
|
|
*
|
|
|
|
* @param pos Center of the sphere or ellipsoid
|
|
|
|
* @param block The block pattern to use
|
|
|
|
* @param radius The sphere's radius
|
|
|
|
* @param filled If false, only a shell will be generated.
|
|
|
|
* @return number of blocks changed
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int makeSphere(BlockVector3 pos, Pattern block, double radius, boolean filled) throws MaxChangedBlocksException {
|
2018-12-20 16:00:27 +00:00
|
|
|
return makeSphere(pos, block, radius, radius, radius, filled);
|
2011-10-26 20:50:46 +00:00
|
|
|
}
|
|
|
|
|
2010-10-18 17:39:32 +00:00
|
|
|
/**
|
2011-09-02 19:38:38 +00:00
|
|
|
* Makes a sphere or ellipsoid.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param pos Center of the sphere or ellipsoid
|
|
|
|
* @param block The block pattern to use
|
2011-09-02 19:38:38 +00:00
|
|
|
* @param radiusX The sphere/ellipsoid's largest north/south extent
|
|
|
|
* @param radiusY The sphere/ellipsoid's largest up/down extent
|
|
|
|
* @param radiusZ The sphere/ellipsoid's largest east/west extent
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param filled If false, only a shell will be generated.
|
2010-10-18 17:39:32 +00:00
|
|
|
* @return number of blocks changed
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-18 17:39:32 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int makeSphere(BlockVector3 pos, Pattern block, double radiusX, double radiusY, double radiusZ, boolean filled) throws MaxChangedBlocksException {
|
2011-09-02 19:38:38 +00:00
|
|
|
radiusX += 0.5;
|
|
|
|
radiusY += 0.5;
|
|
|
|
radiusZ += 0.5;
|
|
|
|
|
|
|
|
final double invRadiusX = 1 / radiusX;
|
|
|
|
final double invRadiusY = 1 / radiusY;
|
|
|
|
final double invRadiusZ = 1 / radiusZ;
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
int px = pos.getBlockX();
|
|
|
|
int py = pos.getBlockY();
|
|
|
|
int pz = pos.getBlockZ();
|
|
|
|
|
2011-09-02 19:38:38 +00:00
|
|
|
final int ceilRadiusX = (int) Math.ceil(radiusX);
|
|
|
|
final int ceilRadiusY = (int) Math.ceil(radiusY);
|
|
|
|
final int ceilRadiusZ = (int) Math.ceil(radiusZ);
|
|
|
|
|
2019-12-21 05:20:17 +00:00
|
|
|
int yy;
|
|
|
|
|
2019-04-16 15:12:09 +00:00
|
|
|
double nextXn = invRadiusX;
|
2019-06-25 13:00:44 +00:00
|
|
|
forX: for (int x = 0; x <= ceilRadiusX; ++x) {
|
2011-09-02 19:38:38 +00:00
|
|
|
final double xn = nextXn;
|
2019-07-17 16:31:13 +00:00
|
|
|
double dx = xn * xn;
|
2011-09-03 16:54:20 +00:00
|
|
|
nextXn = (x + 1) * invRadiusX;
|
2019-04-16 15:12:09 +00:00
|
|
|
double nextZn = invRadiusZ;
|
2019-06-25 13:00:44 +00:00
|
|
|
forZ: for (int z = 0; z <= ceilRadiusZ; ++z) {
|
2019-04-16 15:12:09 +00:00
|
|
|
final double zn = nextZn;
|
2019-06-25 13:00:44 +00:00
|
|
|
double dz = zn * zn;
|
2019-07-17 16:31:13 +00:00
|
|
|
double dxz = dx + dz;
|
2019-04-16 15:12:09 +00:00
|
|
|
nextZn = (z + 1) * invRadiusZ;
|
|
|
|
double nextYn = invRadiusY;
|
|
|
|
|
2019-06-25 13:00:44 +00:00
|
|
|
forY: for (int y = 0; y <= ceilRadiusY; ++y) {
|
2019-04-16 15:12:09 +00:00
|
|
|
final double yn = nextYn;
|
2019-07-17 16:31:13 +00:00
|
|
|
double dy = yn * yn;
|
|
|
|
double dxyz = dxz + dy;
|
2019-04-16 15:12:09 +00:00
|
|
|
nextYn = (y + 1) * invRadiusY;
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
if (dxyz > 1) {
|
2019-04-16 15:12:09 +00:00
|
|
|
if (y == 0) {
|
|
|
|
if (z == 0) {
|
2011-09-02 19:38:38 +00:00
|
|
|
break forX;
|
2011-09-03 16:54:20 +00:00
|
|
|
}
|
2019-04-16 15:12:09 +00:00
|
|
|
break forZ;
|
2011-09-02 19:38:38 +00:00
|
|
|
}
|
2019-04-16 15:12:09 +00:00
|
|
|
break forY;
|
2011-09-02 19:38:38 +00:00
|
|
|
}
|
2011-08-12 06:16:53 +00:00
|
|
|
|
|
|
|
if (!filled) {
|
2019-07-17 16:31:13 +00:00
|
|
|
if (nextXn * nextXn + dy + dz <= 1 && nextYn * nextYn + dx + dz <= 1 && nextZn * nextZn + dx + dy <= 1) {
|
2011-08-12 06:16:53 +00:00
|
|
|
continue;
|
2011-09-03 16:54:20 +00:00
|
|
|
}
|
2011-08-12 06:16:53 +00:00
|
|
|
}
|
2019-12-21 05:20:17 +00:00
|
|
|
yy = py + y;
|
|
|
|
if (yy <= maxY) {
|
|
|
|
this.setBlock(px + x, py + y, pz + z, block);
|
|
|
|
if (x != 0) this.setBlock(px - x, py + y, pz + z, block);
|
|
|
|
if (z != 0) {
|
|
|
|
this.setBlock(px + x, py + y, pz - z, block);
|
|
|
|
if (x != 0) this.setBlock(px - x, py + y, pz - z, block);
|
|
|
|
}
|
2019-04-16 15:12:09 +00:00
|
|
|
}
|
2019-12-21 05:20:17 +00:00
|
|
|
if (y != 0 && (yy = py - y) >= 0) {
|
|
|
|
this.setBlock(px + x, yy, pz + z, block);
|
|
|
|
if (x != 0) this.setBlock(px - x, yy, pz + z, block);
|
2019-04-16 15:12:09 +00:00
|
|
|
if (z != 0) {
|
2019-12-21 05:20:17 +00:00
|
|
|
this.setBlock(px + x, yy, pz - z, block);
|
|
|
|
if (x != 0) this.setBlock(px - x, yy, pz - z, block);
|
2019-04-16 15:12:09 +00:00
|
|
|
}
|
|
|
|
}
|
2010-10-18 17:39:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
return changes;
|
2010-10-18 17:39:32 +00:00
|
|
|
}
|
|
|
|
|
2011-08-07 00:40:48 +00:00
|
|
|
/**
|
|
|
|
* Makes a pyramid.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-07-29 18:04:04 +00:00
|
|
|
* @param position a position
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param block a block
|
|
|
|
* @param size size of pyramid
|
|
|
|
* @param filled true if filled
|
2011-08-07 00:40:48 +00:00
|
|
|
* @return number of blocks changed
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2011-08-07 00:40:48 +00:00
|
|
|
*/
|
2019-03-28 19:02:37 +00:00
|
|
|
public int makePyramid(BlockVector3 position, Pattern block, int size, boolean filled) throws MaxChangedBlocksException {
|
2019-06-28 14:01:51 +00:00
|
|
|
int bx = position.getX();
|
|
|
|
int by = position.getY();
|
|
|
|
int bz = position.getZ();
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
int height = size;
|
2011-09-24 19:32:03 +00:00
|
|
|
|
2011-08-07 00:40:48 +00:00
|
|
|
for (int y = 0; y <= height; ++y) {
|
|
|
|
size--;
|
|
|
|
for (int x = 0; x <= size; ++x) {
|
|
|
|
for (int z = 0; z <= size; ++z) {
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
if ((filled && z <= size && x <= size) || z == size || x == size) {
|
2019-06-28 14:01:51 +00:00
|
|
|
setBlock(x + bx, y + by, z + bz, block);
|
|
|
|
setBlock(-x + bx, y + by, z + bz, block);
|
|
|
|
setBlock(x + bx, y + by, -z + bz, block);
|
|
|
|
setBlock(-x + bx, y + by, -z + bz, block);
|
2011-08-07 00:40:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
return changes;
|
2011-08-07 00:40:48 +00:00
|
|
|
}
|
|
|
|
|
2011-01-08 19:40:18 +00:00
|
|
|
/**
|
2014-07-29 18:04:04 +00:00
|
|
|
* Thaw blocks in a radius.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-07-29 18:04:04 +00:00
|
|
|
* @param position the position
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param radius the radius
|
2011-01-08 19:40:18 +00:00
|
|
|
* @return number of blocks affected
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2011-01-08 19:40:18 +00:00
|
|
|
*/
|
2019-03-28 19:02:37 +00:00
|
|
|
public int thaw(BlockVector3 position, double radius)
|
2018-12-23 16:19:33 +00:00
|
|
|
throws MaxChangedBlocksException {
|
|
|
|
int affected = 0;
|
|
|
|
double radiusSq = radius * radius;
|
2011-01-08 19:40:18 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
int ox = position.getBlockX();
|
|
|
|
int oy = position.getBlockY();
|
|
|
|
int oz = position.getBlockZ();
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
BlockState air = BlockTypes.AIR.getDefaultState();
|
|
|
|
BlockState water = BlockTypes.WATER.getDefaultState();
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
int ceilRadius = (int) Math.ceil(radius);
|
|
|
|
for (int x = ox - ceilRadius; x <= ox + ceilRadius; ++x) {
|
|
|
|
for (int z = oz - ceilRadius; z <= oz + ceilRadius; ++z) {
|
|
|
|
if ((BlockVector3.at(x, oy, z)).distanceSq(position) > radiusSq) {
|
2011-01-08 19:40:18 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-06-28 14:01:51 +00:00
|
|
|
for (int y = maxY; y >= 1; --y) {
|
|
|
|
BlockType id = getBlock(x, y, z).getBlockType();
|
2019-03-28 19:02:37 +00:00
|
|
|
|
|
|
|
if (id == BlockTypes.ICE) {
|
2019-06-28 14:01:51 +00:00
|
|
|
if (setBlock(x, y, z, water)) {
|
2019-03-28 19:02:37 +00:00
|
|
|
++affected;
|
|
|
|
}
|
|
|
|
} else if (id == BlockTypes.SNOW) {
|
2019-06-28 14:01:51 +00:00
|
|
|
if (setBlock(x, y, z, air)) {
|
2019-03-28 19:02:37 +00:00
|
|
|
++affected;
|
|
|
|
}
|
|
|
|
} else if (id.getMaterial().isAir()) {
|
2019-06-04 15:48:30 +00:00
|
|
|
continue;
|
2011-01-08 19:40:18 +00:00
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2011-09-05 01:53:39 +00:00
|
|
|
break;
|
2011-01-08 19:40:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-23 16:07:01 +00:00
|
|
|
return changes = affected;
|
2011-01-08 19:40:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-07 04:26:52 +00:00
|
|
|
/**
|
2014-07-29 18:04:04 +00:00
|
|
|
* Make snow in a radius.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-07-29 18:04:04 +00:00
|
|
|
* @param position a position
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param radius a radius
|
2010-11-07 04:26:52 +00:00
|
|
|
* @return number of blocks affected
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-11-07 04:26:52 +00:00
|
|
|
*/
|
2019-03-28 19:02:37 +00:00
|
|
|
public int simulateSnow(BlockVector3 position, double radius) throws MaxChangedBlocksException {
|
|
|
|
int affected = 0;
|
|
|
|
double radiusSq = radius * radius;
|
2010-11-07 04:26:52 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
int ox = position.getBlockX();
|
|
|
|
int oy = position.getBlockY();
|
|
|
|
int oz = position.getBlockZ();
|
2010-11-07 04:26:52 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
BlockState ice = BlockTypes.ICE.getDefaultState();
|
|
|
|
BlockState snow = BlockTypes.SNOW.getDefaultState();
|
2010-11-07 04:26:52 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
int ceilRadius = (int) Math.ceil(radius);
|
|
|
|
for (int x = ox - ceilRadius; x <= ox + ceilRadius; ++x) {
|
|
|
|
for (int z = oz - ceilRadius; z <= oz + ceilRadius; ++z) {
|
|
|
|
if ((BlockVector3.at(x, oy, z)).distanceSq(position) > radiusSq) {
|
2010-11-07 04:26:52 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-06-28 18:53:44 +00:00
|
|
|
for (int y = maxY; y >= 1; --y) {
|
2019-03-28 19:02:37 +00:00
|
|
|
BlockVector3 pt = BlockVector3.at(x, y, z);
|
|
|
|
BlockType id = getBlock(pt).getBlockType();
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
if (id.getMaterial().isAir()) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
// Ice!
|
|
|
|
if (id == BlockTypes.WATER) {
|
|
|
|
if (setBlock(pt, ice)) {
|
|
|
|
++affected;
|
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
break;
|
2019-03-28 19:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Snow should not cover these blocks
|
2019-06-04 15:48:30 +00:00
|
|
|
if (id.getMaterial().isTranslucent()) {
|
2019-03-28 19:02:37 +00:00
|
|
|
// Add snow on leaves
|
|
|
|
if (!BlockCategories.LEAVES.contains(id)) {
|
2017-12-14 20:00:38 +00:00
|
|
|
break;
|
2019-06-04 15:48:30 +00:00
|
|
|
}
|
2011-09-05 01:53:39 +00:00
|
|
|
}
|
2011-01-01 18:33:18 +00:00
|
|
|
|
2011-09-05 01:53:39 +00:00
|
|
|
// Too high?
|
2019-06-28 14:01:51 +00:00
|
|
|
if (y == maxY) {
|
2019-03-28 19:02:37 +00:00
|
|
|
break;
|
2010-11-07 04:26:52 +00:00
|
|
|
}
|
2011-09-05 01:53:39 +00:00
|
|
|
|
|
|
|
// add snow cover
|
2019-03-28 19:02:37 +00:00
|
|
|
if (setBlock(pt.add(0, 1, 0), snow)) {
|
|
|
|
++affected;
|
|
|
|
}
|
|
|
|
break;
|
2010-11-07 04:26:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-23 16:07:01 +00:00
|
|
|
return changes = affected;
|
2010-11-07 04:26:52 +00:00
|
|
|
}
|
2011-01-01 18:33:18 +00:00
|
|
|
|
2013-12-29 17:28:42 +00:00
|
|
|
/**
|
2014-07-29 18:04:04 +00:00
|
|
|
* Make dirt green.
|
2013-12-29 17:28:42 +00:00
|
|
|
*
|
2014-07-29 18:04:04 +00:00
|
|
|
* @param position a position
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param radius a radius
|
2019-06-04 15:48:30 +00:00
|
|
|
* @param onlyNormalDirt only affect normal dirt (data value 0)
|
2013-12-29 17:28:42 +00:00
|
|
|
* @return number of blocks affected
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2013-12-29 17:28:42 +00:00
|
|
|
*/
|
2019-07-06 00:46:48 +00:00
|
|
|
public int green(BlockVector3 position, double radius, boolean onlyNormalDirt)
|
2018-12-23 16:19:33 +00:00
|
|
|
throws MaxChangedBlocksException {
|
2011-12-10 11:16:57 +00:00
|
|
|
final double radiusSq = radius * radius;
|
2011-08-16 16:43:13 +00:00
|
|
|
|
2014-07-29 18:04:04 +00:00
|
|
|
final int ox = position.getBlockX();
|
|
|
|
final int oz = position.getBlockZ();
|
2011-08-16 16:43:13 +00:00
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
final BlockState grass = BlockTypes.GRASS_BLOCK.getDefaultState();
|
|
|
|
|
2011-12-10 11:16:57 +00:00
|
|
|
final int ceilRadius = (int) Math.ceil(radius);
|
2019-06-04 15:48:30 +00:00
|
|
|
for (int x = ox - ceilRadius; x <= ox + ceilRadius; ++x) {
|
2018-08-12 14:03:07 +00:00
|
|
|
int dx = x - ox;
|
|
|
|
int dx2 = dx * dx;
|
2019-07-06 00:46:48 +00:00
|
|
|
for (int z = oz - ceilRadius; z <= oz + ceilRadius; ++z) {
|
2018-08-12 14:03:07 +00:00
|
|
|
int dz = z - oz;
|
|
|
|
int dz2 = dz * dz;
|
|
|
|
if (dx2 + dz2 > radiusSq) {
|
2011-08-16 16:43:13 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-07-17 16:31:13 +00:00
|
|
|
loop:
|
2019-06-28 14:01:51 +00:00
|
|
|
for (int y = maxY; y >= 1; --y) {
|
2019-07-17 16:31:13 +00:00
|
|
|
final BlockType block = getBlockType(x, y, z);
|
|
|
|
switch (block.getInternalId()) {
|
|
|
|
case BlockID.COARSE_DIRT:
|
|
|
|
if (onlyNormalDirt) break loop;
|
|
|
|
case BlockID.DIRT:
|
|
|
|
this.setBlock(x, y, z, grass);
|
|
|
|
break loop;
|
|
|
|
case BlockID.WATER:
|
|
|
|
case BlockID.LAVA:
|
|
|
|
default:
|
|
|
|
if (block.getMaterial().isMovementBlocker()) {
|
|
|
|
break loop;
|
|
|
|
}
|
2011-08-16 16:43:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
return changes;
|
2011-08-16 16:43:13 +00:00
|
|
|
}
|
|
|
|
|
2018-08-17 08:27:48 +00:00
|
|
|
/**
|
|
|
|
* Makes pumpkin patches randomly in an area around the given position.
|
|
|
|
*
|
|
|
|
* @param position the base position
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param apothem the apothem of the (square) area
|
2018-08-17 08:27:48 +00:00
|
|
|
* @return number of patches created
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
2019-11-21 07:57:32 +00:00
|
|
|
public int makePumpkinPatches(BlockVector3 position, int apothem) throws MaxChangedBlocksException {
|
|
|
|
return makePumpkinPatches(position, apothem, 0.02);
|
|
|
|
}
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
public int makePumpkinPatches(BlockVector3 position, int apothem, double density) throws MaxChangedBlocksException {
|
2014-03-01 23:41:40 +00:00
|
|
|
// We want to generate pumpkins
|
2019-06-04 15:48:30 +00:00
|
|
|
GardenPatchGenerator generator = new GardenPatchGenerator(this);
|
2014-03-01 23:41:40 +00:00
|
|
|
generator.setPlant(GardenPatchGenerator.getPumpkinPattern());
|
2010-10-31 01:20:15 +00:00
|
|
|
|
2014-03-01 23:41:40 +00:00
|
|
|
// In a region of the given radius
|
2019-06-04 15:48:30 +00:00
|
|
|
FlatRegion region = new CuboidRegion(
|
|
|
|
getWorld(), // Causes clamping of Y range
|
|
|
|
position.add(-apothem, -5, -apothem),
|
|
|
|
position.add(apothem, 10, apothem));
|
2018-08-12 14:03:07 +00:00
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
GroundFunction ground = new GroundFunction(new ExistingBlockMask(this), generator);
|
|
|
|
LayerVisitor visitor = new LayerVisitor(region, minimumBlockY(region), maximumBlockY(region), ground);
|
2014-03-30 01:51:33 +00:00
|
|
|
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density));
|
2018-08-12 14:03:07 +00:00
|
|
|
Operations.completeBlindly(visitor);
|
|
|
|
return this.changes = ground.getAffected();
|
2010-10-31 01:20:15 +00:00
|
|
|
}
|
|
|
|
|
2010-10-13 05:38:05 +00:00
|
|
|
/**
|
2010-11-07 04:03:34 +00:00
|
|
|
* Makes a forest.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-07-29 18:04:04 +00:00
|
|
|
* @param basePosition a position
|
|
|
|
* @param size a size
|
|
|
|
* @param density between 0 and 1, inclusive
|
2018-06-19 07:03:09 +00:00
|
|
|
* @param treeType the tree type
|
2010-10-13 05:38:05 +00:00
|
|
|
* @return number of trees created
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2010-10-13 05:38:05 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int makeForest(BlockVector3 basePosition, int size, double density, TreeGenerator.TreeType treeType) throws MaxChangedBlocksException {
|
2019-11-21 07:57:32 +00:00
|
|
|
return makeForest(CuboidRegion.fromCenter(basePosition, size), density, treeType);
|
2010-10-13 05:38:05 +00:00
|
|
|
}
|
2010-11-07 04:03:34 +00:00
|
|
|
|
2019-03-21 01:05:11 +00:00
|
|
|
/**
|
|
|
|
* Makes a forest.
|
|
|
|
*
|
|
|
|
* @param region the region to generate trees in
|
|
|
|
* @param density between 0 and 1, inclusive
|
|
|
|
* @param treeType the tree type
|
|
|
|
* @return number of trees created
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
public int makeForest(Region region, double density, TreeGenerator.TreeType treeType) throws MaxChangedBlocksException {
|
|
|
|
ForestGenerator generator = new ForestGenerator(this, treeType);
|
|
|
|
GroundFunction ground = new GroundFunction(new ExistingBlockMask(this), generator);
|
|
|
|
LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground);
|
|
|
|
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density));
|
|
|
|
Operations.completeLegacy(visitor);
|
|
|
|
return ground.getAffected();
|
2010-10-13 05:38:05 +00:00
|
|
|
}
|
2010-11-07 04:03:34 +00:00
|
|
|
|
2010-11-17 06:59:53 +00:00
|
|
|
/**
|
|
|
|
* Get the block distribution inside a region.
|
2011-09-24 19:32:03 +00:00
|
|
|
*
|
2014-07-29 18:04:04 +00:00
|
|
|
* @param region a region
|
|
|
|
* @return the results
|
2010-11-17 06:59:53 +00:00
|
|
|
*/
|
2018-12-27 00:39:10 +00:00
|
|
|
public List<Countable<BlockState>> getBlockDistribution(Region region, boolean separateStates) {
|
2019-04-03 05:53:34 +00:00
|
|
|
if (separateStates) return getBlockDistributionWithData(region);
|
2019-10-23 04:23:52 +00:00
|
|
|
List<Countable<BlockType>> normalDistr = getBlockDistribution(region);
|
2019-04-03 11:28:57 +00:00
|
|
|
List<Countable<BlockState>> distribution = new ArrayList<>();
|
2019-10-23 04:23:52 +00:00
|
|
|
for (Countable<BlockType> count : normalDistr) {
|
|
|
|
distribution.add(new Countable<>(count.getID().getDefaultState(), count.getAmount()));
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
2013-01-19 14:31:00 +00:00
|
|
|
return distribution;
|
|
|
|
}
|
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
/**
|
|
|
|
* Generate a shape for the given expression.
|
|
|
|
*
|
|
|
|
* @param region the region to generate the shape in
|
|
|
|
* @param zero the coordinate origin for x/y/z variables
|
|
|
|
* @param unit the scale of the x/y/z/ variables
|
|
|
|
* @param pattern the default material to make the shape from
|
|
|
|
* @param expressionString the expression defining the shape
|
|
|
|
* @param hollow whether the shape should be hollow
|
|
|
|
* @return number of blocks changed
|
2020-04-23 16:07:01 +00:00
|
|
|
* @throws ExpressionException Thrown when there's a problem during any stage of the expression compilation or evaluation.
|
|
|
|
* @throws MaxChangedBlocksException Thrown when the block limit has been reached
|
2019-06-04 15:48:30 +00:00
|
|
|
*/
|
|
|
|
public int makeShape(final Region region, final Vector3 zero, final Vector3 unit,
|
2019-06-30 18:56:32 +00:00
|
|
|
final Pattern pattern, final String expressionString, final boolean hollow)
|
|
|
|
throws ExpressionException, MaxChangedBlocksException {
|
|
|
|
return makeShape(region, zero, unit, pattern, expressionString, hollow, WorldEdit.getInstance().getConfiguration().calculationTimeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a shape for the given expression.
|
|
|
|
*
|
|
|
|
* @param region the region to generate the shape in
|
|
|
|
* @param zero the coordinate origin for x/y/z variables
|
|
|
|
* @param unit the scale of the x/y/z/ variables
|
|
|
|
* @param pattern the default material to make the shape from
|
|
|
|
* @param expressionString the expression defining the shape
|
|
|
|
* @param hollow whether the shape should be hollow
|
|
|
|
* @param timeout the time, in milliseconds, to wait for each expression evaluation before halting it. -1 to disable
|
|
|
|
* @return number of blocks changed
|
2020-04-23 16:07:01 +00:00
|
|
|
* @throws ExpressionException Thrown when there's a problem during any stage of the expression compilation or evaluation.
|
|
|
|
* @throws MaxChangedBlocksException Thrown when the block limit has been reached
|
2019-06-30 18:56:32 +00:00
|
|
|
*/
|
|
|
|
public int makeShape(final Region region, final Vector3 zero, final Vector3 unit,
|
2020-03-21 01:39:40 +00:00
|
|
|
final Pattern pattern, final String expressionString, final boolean hollow, final int timeout)
|
2019-06-04 15:48:30 +00:00
|
|
|
throws ExpressionException, MaxChangedBlocksException {
|
2011-11-01 13:57:58 +00:00
|
|
|
final Expression expression = Expression.compile(expressionString, "x", "y", "z", "type", "data");
|
|
|
|
expression.optimize();
|
|
|
|
|
2019-10-19 05:24:56 +00:00
|
|
|
final Variable typeVariable = expression.getSlots().getVariable("type")
|
2020-03-21 01:39:40 +00:00
|
|
|
.orElseThrow(IllegalStateException::new);
|
2019-10-19 05:24:56 +00:00
|
|
|
final Variable dataVariable = expression.getSlots().getVariable("data")
|
2020-03-21 01:39:40 +00:00
|
|
|
.orElseThrow(IllegalStateException::new);
|
2011-11-01 13:57:58 +00:00
|
|
|
|
2013-09-21 12:35:57 +00:00
|
|
|
final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero);
|
|
|
|
expression.setEnvironment(environment);
|
|
|
|
|
2019-03-07 00:58:32 +00:00
|
|
|
final int[] timedOut = {0};
|
2011-11-01 13:57:58 +00:00
|
|
|
final ArbitraryShape shape = new ArbitraryShape(region) {
|
|
|
|
@Override
|
2019-06-30 18:56:32 +00:00
|
|
|
protected BaseBlock getMaterial(int x, int y, int z, BaseBlock defaultMaterial) {
|
2018-10-19 20:13:32 +00:00
|
|
|
final Vector3 current = Vector3.at(x, y, z);
|
2013-09-21 12:35:57 +00:00
|
|
|
environment.setCurrentBlock(current);
|
2018-12-23 16:19:33 +00:00
|
|
|
final Vector3 scaled = current.subtract(zero).divide(unit);
|
2011-11-01 13:57:58 +00:00
|
|
|
|
|
|
|
try {
|
2019-11-20 00:11:54 +00:00
|
|
|
int[] legacy = LegacyMapper.getInstance().getLegacyFromBlock(defaultMaterial.toImmutableState());
|
|
|
|
int typeVar = 0;
|
|
|
|
int dataVar = 0;
|
|
|
|
if (legacy != null) {
|
|
|
|
typeVar = legacy[0];
|
|
|
|
if (legacy.length > 1) {
|
|
|
|
dataVar = legacy[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (expression.evaluate(new double[]{scaled.getX(), scaled.getY(), scaled.getZ(), typeVar, dataVar}, timeout) <= 0) {
|
2011-11-01 13:57:58 +00:00
|
|
|
return null;
|
|
|
|
}
|
2019-11-20 00:11:54 +00:00
|
|
|
int newType = (int) typeVariable.getValue();
|
|
|
|
int newData = (int) dataVariable.getValue();
|
|
|
|
if (newType != typeVar || newData != dataVar) {
|
|
|
|
BlockState state = LegacyMapper.getInstance().getBlockFromLegacy(newType, newData);
|
|
|
|
return state == null ? defaultMaterial : state.toBaseBlock();
|
|
|
|
} else {
|
|
|
|
return defaultMaterial;
|
|
|
|
}
|
2019-06-30 18:56:32 +00:00
|
|
|
} catch (ExpressionTimeoutException e) {
|
|
|
|
timedOut[0] = timedOut[0] + 1;
|
|
|
|
return null;
|
2019-04-02 22:21:02 +00:00
|
|
|
} catch (Exception e) {
|
|
|
|
log.warn("Failed to create shape", e);
|
2011-11-01 13:57:58 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2019-03-07 00:58:32 +00:00
|
|
|
int changed = shape.generate(this, pattern, hollow);
|
|
|
|
if (timedOut[0] > 0) {
|
|
|
|
throw new ExpressionTimeoutException(
|
|
|
|
String.format("%d blocks changed. %d blocks took too long to evaluate (increase with //timeout).",
|
|
|
|
changed, timedOut[0]));
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
2019-03-07 00:58:32 +00:00
|
|
|
return changed;
|
2011-11-01 13:57:58 +00:00
|
|
|
}
|
2011-11-01 14:23:42 +00:00
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
public int deformRegion(final Region region, final Vector3 zero, final Vector3 unit, final String expressionString)
|
|
|
|
throws ExpressionException, MaxChangedBlocksException {
|
2019-06-30 18:56:32 +00:00
|
|
|
return deformRegion(region, zero, unit, expressionString, WorldEdit.getInstance().getConfiguration().calculationTimeout);
|
|
|
|
}
|
|
|
|
|
2019-10-09 20:15:26 +00:00
|
|
|
public int deformRegion(final Region region, final Vector3 zero, final Vector3 unit, final String expressionString,
|
2020-03-21 01:39:40 +00:00
|
|
|
final int timeout) throws ExpressionException, MaxChangedBlocksException {
|
2011-11-01 14:23:42 +00:00
|
|
|
final Expression expression = Expression.compile(expressionString, "x", "y", "z");
|
|
|
|
expression.optimize();
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-10-19 05:24:56 +00:00
|
|
|
final Variable x = expression.getSlots().getVariable("x")
|
|
|
|
.orElseThrow(IllegalStateException::new);
|
|
|
|
final Variable y = expression.getSlots().getVariable("y")
|
|
|
|
.orElseThrow(IllegalStateException::new);
|
|
|
|
final Variable z = expression.getSlots().getVariable("z")
|
|
|
|
.orElseThrow(IllegalStateException::new);
|
2019-07-06 00:46:48 +00:00
|
|
|
|
2013-09-21 12:35:57 +00:00
|
|
|
final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero);
|
|
|
|
expression.setEnvironment(environment);
|
2018-12-23 16:19:33 +00:00
|
|
|
final Vector3 zero2 = zero.add(0.5, 0.5, 0.5);
|
2011-11-02 02:39:02 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() {
|
2020-04-23 16:07:01 +00:00
|
|
|
private final MutableBlockVector3 mutable = new MutableBlockVector3();
|
2011-11-02 02:39:02 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
@Override
|
2018-12-23 16:19:33 +00:00
|
|
|
public boolean apply(BlockVector3 position) throws WorldEditException {
|
2018-08-12 14:03:07 +00:00
|
|
|
try {
|
|
|
|
// offset, scale
|
2019-06-30 18:56:32 +00:00
|
|
|
final Vector3 scaled = position.toVector3().subtract(zero).divide(unit);
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
// transform
|
2019-11-20 00:11:54 +00:00
|
|
|
expression.evaluate(new double[]{scaled.getX(), scaled.getY(), scaled.getZ()}, timeout);
|
2018-08-12 14:03:07 +00:00
|
|
|
int xv = (int) (x.getValue() * unit.getX() + zero2.getX());
|
|
|
|
int yv = (int) (y.getValue() * unit.getY() + zero2.getY());
|
|
|
|
int zv = (int) (z.getValue() * unit.getZ() + zero2.getZ());
|
2019-06-30 18:56:32 +00:00
|
|
|
|
|
|
|
// read block from world
|
2018-08-12 14:03:07 +00:00
|
|
|
return setBlock(position, getBlock(xv, yv, zv));
|
|
|
|
} catch (EvaluationException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
2011-11-01 14:23:42 +00:00
|
|
|
}
|
2019-07-18 06:56:57 +00:00
|
|
|
});
|
2018-08-12 14:03:07 +00:00
|
|
|
Operations.completeBlindly(visitor);
|
|
|
|
changes += visitor.getAffected();
|
|
|
|
return changes;
|
2011-11-01 14:23:42 +00:00
|
|
|
}
|
2011-12-08 09:31:12 +00:00
|
|
|
|
|
|
|
/**
|
2011-12-20 10:20:18 +00:00
|
|
|
* Hollows out the region (Semi-well-defined for non-cuboid selections).
|
2011-12-08 09:31:12 +00:00
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param region the region to hollow out.
|
2011-12-20 10:20:18 +00:00
|
|
|
* @param thickness the thickness of the shell to leave (manhattan distance)
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param pattern The block pattern to use
|
2019-06-04 15:48:30 +00:00
|
|
|
*
|
2011-12-08 09:31:12 +00:00
|
|
|
* @return number of blocks affected
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2011-12-08 09:31:12 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int hollowOutRegion(Region region, int thickness, Pattern pattern) throws MaxChangedBlocksException {
|
2019-07-18 06:56:57 +00:00
|
|
|
return hollowOutRegion(region, thickness, pattern, new SolidBlockMask(this));
|
|
|
|
}
|
|
|
|
|
2019-09-22 01:00:45 +00:00
|
|
|
public int hollowOutRegion(Region region, int thickness, Pattern pattern, Mask mask) {
|
2019-07-18 06:56:57 +00:00
|
|
|
try {
|
2019-06-04 15:48:30 +00:00
|
|
|
final Set<BlockVector3> outside = new LocalBlockVectorSet();
|
2011-12-08 09:31:12 +00:00
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
final BlockVector3 min = region.getMinimumPoint();
|
|
|
|
final BlockVector3 max = region.getMaximumPoint();
|
2011-12-08 09:31:12 +00:00
|
|
|
|
2011-12-20 10:20:18 +00:00
|
|
|
final int minX = min.getBlockX();
|
|
|
|
final int minY = min.getBlockY();
|
|
|
|
final int minZ = min.getBlockZ();
|
|
|
|
final int maxX = max.getBlockX();
|
|
|
|
final int maxY = max.getBlockY();
|
|
|
|
final int maxZ = max.getBlockZ();
|
2011-12-08 09:31:12 +00:00
|
|
|
|
|
|
|
for (int x = minX; x <= maxX; ++x) {
|
|
|
|
for (int y = minY; y <= maxY; ++y) {
|
2019-07-18 06:56:57 +00:00
|
|
|
recurseHollow(region, BlockVector3.at(x, y, minZ), outside, mask);
|
|
|
|
recurseHollow(region, BlockVector3.at(x, y, maxZ), outside, mask);
|
2011-12-08 09:31:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int y = minY; y <= maxY; ++y) {
|
|
|
|
for (int z = minZ; z <= maxZ; ++z) {
|
2019-07-18 06:56:57 +00:00
|
|
|
recurseHollow(region, BlockVector3.at(minX, y, z), outside, mask);
|
|
|
|
recurseHollow(region, BlockVector3.at(maxX, y, z), outside, mask);
|
2011-12-08 09:31:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int z = minZ; z <= maxZ; ++z) {
|
|
|
|
for (int x = minX; x <= maxX; ++x) {
|
2019-07-18 06:56:57 +00:00
|
|
|
recurseHollow(region, BlockVector3.at(x, minY, z), outside, mask);
|
|
|
|
recurseHollow(region, BlockVector3.at(x, maxY, z), outside, mask);
|
2011-12-08 09:31:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-19 10:58:38 +00:00
|
|
|
for (int i = 1; i < thickness; ++i) {
|
2019-06-04 15:48:30 +00:00
|
|
|
final Set<BlockVector3> newOutside = new LocalBlockVectorSet();
|
2018-12-23 16:19:33 +00:00
|
|
|
outer: for (BlockVector3 position : region) {
|
|
|
|
for (BlockVector3 recurseDirection : recurseDirections) {
|
|
|
|
BlockVector3 neighbor = position.add(recurseDirection);
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2011-12-19 10:58:38 +00:00
|
|
|
if (outside.contains(neighbor)) {
|
|
|
|
newOutside.add(position);
|
|
|
|
continue outer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2011-12-20 10:20:18 +00:00
|
|
|
outside.addAll(newOutside);
|
2011-12-19 10:58:38 +00:00
|
|
|
}
|
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
outer: for (BlockVector3 position : region) {
|
|
|
|
for (BlockVector3 recurseDirection : recurseDirections) {
|
|
|
|
BlockVector3 neighbor = position.add(recurseDirection);
|
2019-06-04 15:48:30 +00:00
|
|
|
if (outside.contains(neighbor)) {
|
|
|
|
continue outer;
|
2011-12-08 09:31:12 +00:00
|
|
|
}
|
|
|
|
}
|
2019-07-18 06:56:57 +00:00
|
|
|
this.changes++;
|
2020-03-21 01:39:40 +00:00
|
|
|
pattern.apply(getExtent(), position, position);
|
|
|
|
}
|
2019-07-18 06:56:57 +00:00
|
|
|
} catch (WorldEditException e) {
|
|
|
|
throw new RuntimeException(e);
|
2011-12-08 09:31:12 +00:00
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
return changes;
|
|
|
|
}
|
|
|
|
|
2019-09-22 01:00:45 +00:00
|
|
|
public int drawLine(Pattern pattern, BlockVector3 pos1, BlockVector3 pos2, double radius, boolean filled) throws MaxChangedBlocksException {
|
2018-08-12 14:03:07 +00:00
|
|
|
return drawLine(pattern, pos1, pos2, radius, filled, false);
|
2011-12-08 09:31:12 +00:00
|
|
|
}
|
|
|
|
|
2013-07-26 05:45:17 +00:00
|
|
|
/**
|
|
|
|
* Draws a line (out of blocks) between two vectors.
|
|
|
|
*
|
2013-08-05 16:53:30 +00:00
|
|
|
* @param pattern The block pattern used to draw the line.
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param pos1 One of the points that define the line.
|
|
|
|
* @param pos2 The other point that defines the line.
|
|
|
|
* @param radius The radius (thickness) of the line.
|
|
|
|
* @param filled If false, only a shell will be generated.
|
|
|
|
*
|
2013-07-26 05:45:17 +00:00
|
|
|
* @return number of blocks affected
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2020-02-12 21:18:36 +00:00
|
|
|
*
|
|
|
|
* @see #drawLine(Pattern, List, double, boolean)
|
2013-07-26 05:45:17 +00:00
|
|
|
*/
|
2019-06-30 18:56:32 +00:00
|
|
|
public int drawLine(Pattern pattern, BlockVector3 pos1, BlockVector3 pos2, double radius, boolean filled, boolean flat)
|
|
|
|
throws MaxChangedBlocksException {
|
2013-07-26 05:45:17 +00:00
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
LocalBlockVectorSet vset = new LocalBlockVectorSet();
|
2013-07-26 05:45:17 +00:00
|
|
|
boolean notdrawn = true;
|
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
int x1 = pos1.getBlockX(), y1 = pos1.getBlockY(), z1 = pos1.getBlockZ();
|
|
|
|
int x2 = pos2.getBlockX(), y2 = pos2.getBlockY(), z2 = pos2.getBlockZ();
|
2013-07-26 05:45:17 +00:00
|
|
|
int tipx = x1, tipy = y1, tipz = z1;
|
2019-03-28 19:02:37 +00:00
|
|
|
int dx = Math.abs(x2 - x1), dy = Math.abs(y2 - y1), dz = Math.abs(z2 - z1);
|
2013-07-26 05:45:17 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
if (dx + dy + dz == 0) {
|
|
|
|
vset.add(BlockVector3.at(tipx, tipy, tipz));
|
2013-08-04 01:42:19 +00:00
|
|
|
notdrawn = false;
|
2013-07-26 05:45:17 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 21:18:36 +00:00
|
|
|
int dMax = Math.max(Math.max(dx, dy), dz);
|
|
|
|
if (dMax == dx && notdrawn) {
|
2013-07-26 05:45:17 +00:00
|
|
|
for (int domstep = 0; domstep <= dx; domstep++) {
|
2019-03-28 19:02:37 +00:00
|
|
|
tipx = x1 + domstep * (x2 - x1 > 0 ? 1 : -1);
|
2019-09-22 01:00:45 +00:00
|
|
|
tipy = (int) Math.round(y1 + domstep * (double) dy / (double) dx * (y2 - y1 > 0 ? 1 : -1));
|
|
|
|
tipz = (int) Math.round(z1 + domstep * (double) dz / (double) dx * (z2 - z1 > 0 ? 1 : -1));
|
2019-03-28 19:02:37 +00:00
|
|
|
|
|
|
|
vset.add(BlockVector3.at(tipx, tipy, tipz));
|
2013-07-26 05:45:17 +00:00
|
|
|
}
|
2020-02-12 21:18:36 +00:00
|
|
|
} else if (dMax == dy && notdrawn) {
|
2013-07-26 05:45:17 +00:00
|
|
|
for (int domstep = 0; domstep <= dy; domstep++) {
|
2019-03-28 19:02:37 +00:00
|
|
|
tipy = y1 + domstep * (y2 - y1 > 0 ? 1 : -1);
|
2019-09-22 01:00:45 +00:00
|
|
|
tipx = (int) Math.round(x1 + domstep * (double) dx / (double) dy * (x2 - x1 > 0 ? 1 : -1));
|
|
|
|
tipz = (int) Math.round(z1 + domstep * (double) dz / (double) dy * (z2 - z1 > 0 ? 1 : -1));
|
2013-07-26 05:45:17 +00:00
|
|
|
|
2019-03-28 19:02:37 +00:00
|
|
|
vset.add(BlockVector3.at(tipx, tipy, tipz));
|
2013-07-26 05:45:17 +00:00
|
|
|
}
|
2020-02-12 21:18:36 +00:00
|
|
|
} else if (dMax == dz && notdrawn) {
|
2013-07-26 05:45:17 +00:00
|
|
|
for (int domstep = 0; domstep <= dz; domstep++) {
|
2019-03-28 19:02:37 +00:00
|
|
|
tipz = z1 + domstep * (z2 - z1 > 0 ? 1 : -1);
|
2019-09-22 01:00:45 +00:00
|
|
|
tipy = (int) Math.round(y1 + domstep * (double) dy / (double) dz * (y2-y1>0 ? 1 : -1));
|
|
|
|
tipx = (int) Math.round(x1 + domstep * (double) dx / (double) dz * (x2-x1>0 ? 1 : -1));
|
2019-03-28 19:02:37 +00:00
|
|
|
|
|
|
|
vset.add(BlockVector3.at(tipx, tipy, tipz));
|
2013-07-26 05:45:17 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-23 16:19:33 +00:00
|
|
|
Set<BlockVector3> newVset;
|
2018-08-12 14:03:07 +00:00
|
|
|
if (flat) {
|
2019-07-06 00:46:48 +00:00
|
|
|
newVset = getStretched(vset, radius);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (!filled) {
|
|
|
|
newVset = this.getOutline(newVset);
|
|
|
|
}
|
|
|
|
} else {
|
2019-07-06 00:46:48 +00:00
|
|
|
newVset = getBallooned(vset, radius);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (!filled) {
|
|
|
|
newVset = this.getHollowed(newVset);
|
|
|
|
}
|
2013-08-05 16:53:30 +00:00
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
return setBlocks(newVset, pattern);
|
2013-08-05 16:53:30 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 21:18:36 +00:00
|
|
|
/**
|
|
|
|
* Draws a line (out of blocks) between two or more vectors.
|
|
|
|
*
|
|
|
|
* @param pattern The block pattern used to draw the line.
|
|
|
|
* @param vectors the list of vectors to draw the line between
|
|
|
|
* @param radius The radius (thickness) of the line.
|
|
|
|
* @param filled If false, only a shell will be generated.
|
|
|
|
*
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
|
|
|
*/
|
|
|
|
public int drawLine(Pattern pattern, List<BlockVector3> vectors, double radius, boolean filled)
|
2020-03-21 01:39:40 +00:00
|
|
|
throws MaxChangedBlocksException {
|
2020-02-12 21:18:36 +00:00
|
|
|
|
|
|
|
Set<BlockVector3> vset = new HashSet<>();
|
|
|
|
|
|
|
|
for (int i = 0; vectors.size() != 0 && i < vectors.size() - 1; i++) {
|
|
|
|
BlockVector3 pos1 = vectors.get(i);
|
|
|
|
BlockVector3 pos2 = vectors.get(i + 1);
|
|
|
|
|
|
|
|
int x1 = pos1.getBlockX(), y1 = pos1.getBlockY(), z1 = pos1.getBlockZ();
|
|
|
|
int x2 = pos2.getBlockX(), y2 = pos2.getBlockY(), z2 = pos2.getBlockZ();
|
|
|
|
int tipx = x1, tipy = y1, tipz = z1;
|
|
|
|
int dx = Math.abs(x2 - x1), dy = Math.abs(y2 - y1), dz = Math.abs(z2 - z1);
|
|
|
|
|
|
|
|
if (dx + dy + dz == 0) {
|
|
|
|
vset.add(BlockVector3.at(tipx, tipy, tipz));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dMax = Math.max(Math.max(dx, dy), dz);
|
|
|
|
if (dMax == dx) {
|
|
|
|
for (int domstep = 0; domstep <= dx; domstep++) {
|
|
|
|
tipx = x1 + domstep * (x2 - x1 > 0 ? 1 : -1);
|
|
|
|
tipy = (int) Math.round(y1 + domstep * ((double) dy) / ((double) dx) * (y2 - y1 > 0 ? 1 : -1));
|
|
|
|
tipz = (int) Math.round(z1 + domstep * ((double) dz) / ((double) dx) * (z2 - z1 > 0 ? 1 : -1));
|
|
|
|
|
|
|
|
vset.add(BlockVector3.at(tipx, tipy, tipz));
|
|
|
|
}
|
|
|
|
} else if (dMax == dy) {
|
|
|
|
for (int domstep = 0; domstep <= dy; domstep++) {
|
|
|
|
tipy = y1 + domstep * (y2 - y1 > 0 ? 1 : -1);
|
|
|
|
tipx = (int) Math.round(x1 + domstep * ((double) dx) / ((double) dy) * (x2 - x1 > 0 ? 1 : -1));
|
|
|
|
tipz = (int) Math.round(z1 + domstep * ((double) dz) / ((double) dy) * (z2 - z1 > 0 ? 1 : -1));
|
|
|
|
|
|
|
|
vset.add(BlockVector3.at(tipx, tipy, tipz));
|
|
|
|
}
|
|
|
|
} else /* if (dMax == dz) */ {
|
|
|
|
for (int domstep = 0; domstep <= dz; domstep++) {
|
|
|
|
tipz = z1 + domstep * (z2 - z1 > 0 ? 1 : -1);
|
|
|
|
tipy = (int) Math.round(y1 + domstep * ((double) dy) / ((double) dz) * (y2-y1>0 ? 1 : -1));
|
|
|
|
tipx = (int) Math.round(x1 + domstep * ((double) dx) / ((double) dz) * (x2-x1>0 ? 1 : -1));
|
|
|
|
|
|
|
|
vset.add(BlockVector3.at(tipx, tipy, tipz));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vset = getBallooned(vset, radius);
|
|
|
|
if (!filled) {
|
|
|
|
vset = getHollowed(vset);
|
|
|
|
}
|
|
|
|
return setBlocks(vset, pattern);
|
|
|
|
}
|
|
|
|
|
2013-08-05 16:53:30 +00:00
|
|
|
/**
|
|
|
|
* Draws a spline (out of blocks) between specified vectors.
|
|
|
|
*
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param pattern The block pattern used to draw the spline.
|
2013-08-05 16:53:30 +00:00
|
|
|
* @param nodevectors The list of vectors to draw through.
|
2018-12-20 16:00:27 +00:00
|
|
|
* @param tension The tension of every node.
|
|
|
|
* @param bias The bias of every node.
|
|
|
|
* @param continuity The continuity of every node.
|
|
|
|
* @param quality The quality of the spline. Must be greater than 0.
|
|
|
|
* @param radius The radius (thickness) of the spline.
|
|
|
|
* @param filled If false, only a shell will be generated.
|
|
|
|
*
|
2013-08-05 16:53:30 +00:00
|
|
|
* @return number of blocks affected
|
2014-07-29 21:23:00 +00:00
|
|
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
2013-08-05 16:53:30 +00:00
|
|
|
*/
|
2019-06-04 15:48:30 +00:00
|
|
|
public int drawSpline(Pattern pattern, List<BlockVector3> nodevectors, double tension, double bias,
|
|
|
|
double continuity, double quality, double radius, boolean filled)
|
|
|
|
throws MaxChangedBlocksException {
|
|
|
|
|
2018-08-12 14:03:07 +00:00
|
|
|
LocalBlockVectorSet vset = new LocalBlockVectorSet();
|
2019-06-04 15:48:30 +00:00
|
|
|
List<Node> nodes = new ArrayList<>(nodevectors.size());
|
|
|
|
|
|
|
|
Interpolation interpol = new KochanekBartelsInterpolation();
|
2013-08-05 16:53:30 +00:00
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
for (BlockVector3 nodevector : nodevectors) {
|
|
|
|
Node n = new Node(nodevector.toVector3());
|
2013-08-05 16:53:30 +00:00
|
|
|
n.setTension(tension);
|
|
|
|
n.setBias(bias);
|
|
|
|
n.setContinuity(continuity);
|
|
|
|
nodes.add(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
interpol.setNodes(nodes);
|
2019-06-04 15:48:30 +00:00
|
|
|
double splinelength = interpol.arcLength(0, 1);
|
2013-08-05 16:53:30 +00:00
|
|
|
for (double loop = 0; loop <= 1; loop += 1D / splinelength / quality) {
|
2019-07-06 00:46:48 +00:00
|
|
|
BlockVector3 tipv = interpol.getPosition(loop).toBlockPoint();
|
2018-08-12 14:03:07 +00:00
|
|
|
if (radius == 0) {
|
2019-07-17 16:31:13 +00:00
|
|
|
pattern.apply(this, tipv, tipv);
|
2018-08-12 14:03:07 +00:00
|
|
|
} else {
|
|
|
|
vset.add(tipv);
|
|
|
|
}
|
2013-08-05 16:53:30 +00:00
|
|
|
}
|
2018-12-23 16:19:33 +00:00
|
|
|
Set<BlockVector3> newVset;
|
2018-08-12 14:03:07 +00:00
|
|
|
if (radius != 0) {
|
2019-07-06 00:46:48 +00:00
|
|
|
newVset = getBallooned(vset, radius);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (!filled) {
|
|
|
|
newVset = this.getHollowed(newVset);
|
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
return setBlocks(newVset, pattern);
|
2013-08-04 01:42:19 +00:00
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
return changes;
|
2013-08-04 01:42:19 +00:00
|
|
|
}
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
private static Set<BlockVector3> getBallooned(Set<BlockVector3> vset, double radius) {
|
2018-08-12 14:03:07 +00:00
|
|
|
if (radius < 1) {
|
|
|
|
return vset;
|
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
LocalBlockVectorSet returnset = new LocalBlockVectorSet();
|
|
|
|
int ceilrad = (int) Math.ceil(radius);
|
|
|
|
|
|
|
|
for (BlockVector3 v : vset) {
|
|
|
|
int tipx = v.getBlockX(), tipy = v.getBlockY(), tipz = v.getBlockZ();
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
for (int loopx = tipx - ceilrad; loopx <= tipx + ceilrad; loopx++) {
|
|
|
|
for (int loopy = tipy - ceilrad; loopy <= tipy + ceilrad; loopy++) {
|
|
|
|
for (int loopz = tipz - ceilrad; loopz <= tipz + ceilrad; loopz++) {
|
2018-08-12 14:03:07 +00:00
|
|
|
if (MathMan.hypot(loopx - tipx, loopy - tipy, loopz - tipz) <= radius) {
|
|
|
|
returnset.add(loopx, loopy, loopz);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-04 01:42:19 +00:00
|
|
|
}
|
2018-08-12 14:03:07 +00:00
|
|
|
return returnset;
|
2013-08-04 01:42:19 +00:00
|
|
|
}
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
public static Set<BlockVector3> getStretched(Set<BlockVector3> vset, double radius) {
|
2018-08-12 14:03:07 +00:00
|
|
|
if (radius < 1) {
|
|
|
|
return vset;
|
|
|
|
}
|
|
|
|
final LocalBlockVectorSet returnset = new LocalBlockVectorSet();
|
|
|
|
final int ceilrad = (int) Math.ceil(radius);
|
2019-09-22 01:00:45 +00:00
|
|
|
for (BlockVector3 v : vset) {
|
2018-08-12 14:03:07 +00:00
|
|
|
final int tipx = v.getBlockX(), tipy = v.getBlockY(), tipz = v.getBlockZ();
|
2019-07-06 00:46:48 +00:00
|
|
|
for (int loopx = tipx - ceilrad; loopx <= tipx + ceilrad; loopx++) {
|
|
|
|
for (int loopz = tipz - ceilrad; loopz <= tipz + ceilrad; loopz++) {
|
2018-08-12 14:03:07 +00:00
|
|
|
if (MathMan.hypot(loopx - tipx, 0, loopz - tipz) <= radius) {
|
|
|
|
returnset.add(loopx, v.getBlockY(), loopz);
|
2013-08-05 16:53:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return returnset;
|
|
|
|
}
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
public Set<BlockVector3> getOutline(Set<BlockVector3> vset) {
|
2018-08-12 14:03:07 +00:00
|
|
|
final LocalBlockVectorSet returnset = new LocalBlockVectorSet();
|
2018-12-23 16:19:33 +00:00
|
|
|
final LocalBlockVectorSet newset = new LocalBlockVectorSet();
|
|
|
|
newset.addAll(vset);
|
2019-06-30 18:56:32 +00:00
|
|
|
for (BlockVector3 v : newset) {
|
2018-12-23 16:19:33 +00:00
|
|
|
final int x = v.getX(), y = v.getY(), z = v.getZ();
|
2019-06-30 18:56:32 +00:00
|
|
|
if (!(newset.contains(x + 1, y, z) &&
|
|
|
|
newset.contains(x - 1, y, z) &&
|
|
|
|
newset.contains(x, y, z + 1) &&
|
|
|
|
newset.contains(x, y, z - 1))) {
|
2018-08-12 14:03:07 +00:00
|
|
|
returnset.add(v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return returnset;
|
|
|
|
}
|
|
|
|
|
2019-07-06 00:46:48 +00:00
|
|
|
public Set<BlockVector3> getHollowed(Set<BlockVector3> vset) {
|
2019-06-30 18:56:32 +00:00
|
|
|
final Set<BlockVector3> returnset = new LocalBlockVectorSet();
|
2018-12-23 16:19:33 +00:00
|
|
|
final LocalBlockVectorSet newset = new LocalBlockVectorSet();
|
|
|
|
newset.addAll(vset);
|
2019-06-30 18:56:32 +00:00
|
|
|
for (BlockVector3 v : newset) {
|
2018-12-23 16:19:33 +00:00
|
|
|
final int x = v.getX(), y = v.getY(), z = v.getZ();
|
2019-06-30 18:56:32 +00:00
|
|
|
if (!(newset.contains(x + 1, y, z) &&
|
|
|
|
newset.contains(x - 1, y, z) &&
|
|
|
|
newset.contains(x, y + 1, z) &&
|
|
|
|
newset.contains(x, y - 1, z) &&
|
|
|
|
newset.contains(x, y, z + 1) &&
|
|
|
|
newset.contains(x, y, z - 1))) {
|
2013-08-04 01:42:19 +00:00
|
|
|
returnset.add(v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return returnset;
|
|
|
|
}
|
|
|
|
|
2019-07-18 06:56:57 +00:00
|
|
|
private void recurseHollow(Region region, BlockVector3 origin, Set<BlockVector3> outside, Mask mask) {
|
2019-04-05 14:01:05 +00:00
|
|
|
final LocalBlockVectorSet queue = new LocalBlockVectorSet();
|
2019-06-08 18:48:30 +00:00
|
|
|
queue.add(origin);
|
2019-07-06 00:46:48 +00:00
|
|
|
|
2011-12-08 09:31:12 +00:00
|
|
|
while (!queue.isEmpty()) {
|
2019-07-18 06:56:57 +00:00
|
|
|
Iterator<BlockVector3> iter = queue.iterator();
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
final BlockVector3 current = iter.next();
|
|
|
|
iter.remove();
|
2020-01-04 10:11:13 +00:00
|
|
|
if (mask.test(getExtent(), current)) {
|
2019-07-18 06:56:57 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!outside.add(current)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!region.contains(current)) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-06-12 21:12:12 +00:00
|
|
|
|
2019-07-18 06:56:57 +00:00
|
|
|
for (BlockVector3 recurseDirection : recurseDirections) {
|
|
|
|
queue.add(current.add(recurseDirection));
|
|
|
|
}
|
|
|
|
}
|
2019-06-12 21:12:12 +00:00
|
|
|
}
|
2011-12-08 09:31:12 +00:00
|
|
|
}
|
2013-01-19 14:31:00 +00:00
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
public int makeBiomeShape(final Region region, final Vector3 zero, final Vector3 unit, final BiomeType biomeType,
|
|
|
|
final String expressionString, final boolean hollow)
|
|
|
|
throws ExpressionException, MaxChangedBlocksException {
|
2019-06-30 18:56:32 +00:00
|
|
|
return makeBiomeShape(region, zero, unit, biomeType, expressionString, hollow, WorldEdit.getInstance().getConfiguration().calculationTimeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
public int makeBiomeShape(final Region region, final Vector3 zero, final Vector3 unit, final BiomeType biomeType,
|
|
|
|
final String expressionString, final boolean hollow, final int timeout)
|
|
|
|
throws ExpressionException, MaxChangedBlocksException {
|
2018-12-23 16:19:33 +00:00
|
|
|
final Vector2 zero2D = zero.toVector2();
|
|
|
|
final Vector2 unit2D = unit.toVector2();
|
2013-10-20 14:26:06 +00:00
|
|
|
|
|
|
|
final Expression expression = Expression.compile(expressionString, "x", "z");
|
|
|
|
expression.optimize();
|
|
|
|
|
|
|
|
final EditSession editSession = this;
|
|
|
|
final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(editSession, unit, zero);
|
|
|
|
expression.setEnvironment(environment);
|
|
|
|
|
2019-06-30 18:56:32 +00:00
|
|
|
final int[] timedOut = {0};
|
2013-10-20 14:26:06 +00:00
|
|
|
final ArbitraryBiomeShape shape = new ArbitraryBiomeShape(region) {
|
|
|
|
@Override
|
2019-06-30 18:56:32 +00:00
|
|
|
protected BiomeType getBiome(int x, int z, BiomeType defaultBiomeType) {
|
2019-03-31 16:09:20 +00:00
|
|
|
environment.setCurrentBlock(x, 0, z);
|
|
|
|
double scaledX = (x - zero2D.getX()) / unit2D.getX();
|
|
|
|
double scaledZ = (z - zero2D.getZ()) / unit2D.getZ();
|
2013-10-20 14:26:06 +00:00
|
|
|
|
|
|
|
try {
|
2019-10-23 04:23:52 +00:00
|
|
|
if (expression.evaluate(timeout, scaledX, scaledZ) <= 0) {
|
2018-08-12 14:03:07 +00:00
|
|
|
return null;
|
2013-10-20 14:26:06 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 15:48:30 +00:00
|
|
|
// TODO: Allow biome setting via a script variable (needs BiomeType<->int mapping)
|
2013-10-20 14:26:06 +00:00
|
|
|
return defaultBiomeType;
|
2019-06-30 18:56:32 +00:00
|
|
|
} catch (ExpressionTimeoutException e) {
|
|
|
|
timedOut[0] = timedOut[0] + 1;
|
|
|
|
return null;
|
2019-04-02 22:21:02 +00:00
|
|
|
} catch (Exception e) {
|
|
|
|
log.warn("Failed to create shape", e);
|
2013-10-20 14:26:06 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2019-03-07 00:58:32 +00:00
|
|
|
int changed = shape.generate(this, biomeType, hollow);
|
2019-06-30 18:56:32 +00:00
|
|
|
if (timedOut[0] > 0) {
|
|
|
|
throw new ExpressionTimeoutException(
|
|
|
|
String.format("%d blocks changed. %d blocks took too long to evaluate (increase time with //timeout)",
|
|
|
|
changed, timedOut[0]));
|
|
|
|
}
|
2019-03-07 00:58:32 +00:00
|
|
|
return changed;
|
2013-10-20 14:26:06 +00:00
|
|
|
}
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
private static final BlockVector3[] recurseDirections = {
|
|
|
|
Direction.NORTH.toBlockVector(),
|
|
|
|
Direction.EAST.toBlockVector(),
|
|
|
|
Direction.SOUTH.toBlockVector(),
|
|
|
|
Direction.WEST.toBlockVector(),
|
|
|
|
Direction.UP.toBlockVector(),
|
|
|
|
Direction.DOWN.toBlockVector(),
|
|
|
|
};
|
2019-06-04 15:48:30 +00:00
|
|
|
|
2019-09-22 01:00:45 +00:00
|
|
|
public boolean regenerate(Region region) {
|
2018-08-12 14:03:07 +00:00
|
|
|
return regenerate(region, this);
|
|
|
|
}
|
|
|
|
|
2019-09-22 01:00:45 +00:00
|
|
|
public boolean regenerate(Region region, EditSession session) {
|
2018-08-12 14:03:07 +00:00
|
|
|
return session.regenerate(region, null, null);
|
|
|
|
}
|
|
|
|
|
2018-12-23 16:19:33 +00:00
|
|
|
private void setExistingBlocks(BlockVector3 pos1, BlockVector3 pos2) {
|
2019-04-03 03:36:32 +00:00
|
|
|
for (int x = pos1.getX(); x <= pos2.getX(); x++) {
|
2018-08-12 14:03:07 +00:00
|
|
|
for (int z = pos1.getBlockZ(); z <= pos2.getBlockZ(); z++) {
|
2019-04-03 03:36:32 +00:00
|
|
|
for (int y = pos1.getY(); y <= pos2.getY(); y++) {
|
2019-07-18 06:56:57 +00:00
|
|
|
setBlock(x, y, z, getFullBlock(x, y, z));
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-22 01:00:45 +00:00
|
|
|
public boolean regenerate(Region region, BiomeType biome, Long seed) {
|
2018-08-12 14:03:07 +00:00
|
|
|
//TODO Optimize - avoid Vector2D creation (make mutable)
|
2020-01-06 08:36:16 +00:00
|
|
|
final AbstractChangeSet fcs = (AbstractChangeSet) this.getChangeSet();
|
2019-07-18 16:07:31 +00:00
|
|
|
this.setChangeSet(null);
|
2018-08-12 14:03:07 +00:00
|
|
|
final FaweRegionExtent fe = this.getRegionExtent();
|
|
|
|
final boolean cuboid = region instanceof CuboidRegion;
|
|
|
|
if (fe != null && cuboid) {
|
2019-07-22 20:42:40 +00:00
|
|
|
BlockVector3 max = region.getMaximumPoint();
|
|
|
|
BlockVector3 min = region.getMinimumPoint();
|
2018-08-12 14:03:07 +00:00
|
|
|
if (!fe.contains(max.getBlockX(), max.getBlockY(), max.getBlockZ()) && !fe.contains(min.getBlockX(), min.getBlockY(), min.getBlockZ())) {
|
2020-01-29 20:01:38 +00:00
|
|
|
throw FaweCache.OUTSIDE_REGION;
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-23 16:19:33 +00:00
|
|
|
final Set<BlockVector2> chunks = region.getChunks();
|
2019-03-31 16:09:20 +00:00
|
|
|
MutableBlockVector3 mutable = new MutableBlockVector3();
|
|
|
|
MutableBlockVector2 mutable2D = new MutableBlockVector2();
|
2018-12-23 16:19:33 +00:00
|
|
|
for (BlockVector2 chunk : chunks) {
|
2018-08-12 14:03:07 +00:00
|
|
|
final int cx = chunk.getBlockX();
|
|
|
|
final int cz = chunk.getBlockZ();
|
|
|
|
final int bx = cx << 4;
|
|
|
|
final int bz = cz << 4;
|
2018-10-19 20:13:32 +00:00
|
|
|
final BlockVector3 cmin = BlockVector3.at(bx, 0, bz);
|
2019-04-05 06:51:42 +00:00
|
|
|
final BlockVector3 cmax = cmin.add(15, maxY, 15);
|
2019-09-22 01:00:45 +00:00
|
|
|
final boolean containsBot1 =
|
|
|
|
fe == null || fe.contains(cmin.getBlockX(), cmin.getBlockY(), cmin.getBlockZ());
|
2018-08-12 14:03:07 +00:00
|
|
|
final boolean containsBot2 = region.contains(cmin);
|
2019-09-22 01:00:45 +00:00
|
|
|
final boolean containsTop1 =
|
|
|
|
fe == null || fe.contains(cmax.getBlockX(), cmax.getBlockY(), cmax.getBlockZ());
|
2018-08-12 14:03:07 +00:00
|
|
|
final boolean containsTop2 = region.contains(cmax);
|
2019-09-22 01:00:45 +00:00
|
|
|
if (containsBot2 && containsTop2 && !containsBot1 && !containsTop1) {
|
2018-08-12 14:03:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
boolean conNextX = chunks.contains(mutable2D.setComponents(cx + 1, cz));
|
|
|
|
boolean conNextZ = chunks.contains(mutable2D.setComponents(cx, cz + 1));
|
|
|
|
boolean containsAny = false;
|
|
|
|
if (cuboid && containsBot1 && containsBot2 && containsTop1 && containsTop2 && conNextX && conNextZ) {
|
|
|
|
containsAny = true;
|
|
|
|
if (fcs != null) {
|
|
|
|
for (int x = 0; x < 16; x++) {
|
|
|
|
int xx = x + bx;
|
|
|
|
for (int z = 0; z < 16; z++) {
|
|
|
|
int zz = z + bz;
|
2019-04-05 06:51:42 +00:00
|
|
|
for (int y = 0; y < maxY + 1; y++) {
|
2019-04-03 11:28:57 +00:00
|
|
|
BaseBlock block = getFullBlock(mutable.setComponents(xx, y, zz));
|
2019-06-30 18:56:32 +00:00
|
|
|
fcs.add(mutable, block, BlockTypes.AIR.getDefaultState().toBaseBlock());
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!conNextX) {
|
2019-04-05 06:51:42 +00:00
|
|
|
setExistingBlocks(BlockVector3.at(bx + 16, 0, bz), BlockVector3.at(bx + 31, maxY, bz + 15));
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
if (!conNextZ) {
|
2019-04-05 06:51:42 +00:00
|
|
|
setExistingBlocks(BlockVector3.at(bx, 0, bz + 16), BlockVector3.at(bx + 15, maxY, bz + 31));
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
if (!chunks.contains(mutable2D.setComponents(cx + 1, cz + 1)) && !conNextX && !conNextZ) {
|
2019-04-05 06:51:42 +00:00
|
|
|
setExistingBlocks(BlockVector3.at(bx + 16, 0, bz + 16), BlockVector3.at(bx + 31, maxY, bz + 31));
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
for (int x = 0; x < 16; x++) {
|
|
|
|
int xx = x + bx;
|
|
|
|
mutable.mutX(xx);
|
|
|
|
for (int z = 0; z < 16; z++) {
|
|
|
|
int zz = z + bz;
|
|
|
|
mutable.mutZ(zz);
|
2019-04-05 06:51:42 +00:00
|
|
|
for (int y = 0; y < maxY + 1; y++) {
|
2018-08-12 14:03:07 +00:00
|
|
|
mutable.mutY(y);
|
2019-04-03 11:28:57 +00:00
|
|
|
boolean contains = (fe == null || fe.contains(xx, y, zz)) && region.contains(mutable);
|
2018-08-12 14:03:07 +00:00
|
|
|
if (contains) {
|
|
|
|
containsAny = true;
|
|
|
|
if (fcs != null) {
|
2019-04-03 11:28:57 +00:00
|
|
|
BaseBlock block = getFullBlock(mutable);
|
|
|
|
fcs.add(mutable, block, BlockTypes.AIR.getDefaultState().toBaseBlock());
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
} else {
|
2019-09-22 01:00:45 +00:00
|
|
|
BaseBlock block = getFullBlock(mutable);
|
2018-08-12 14:03:07 +00:00
|
|
|
try {
|
2019-04-03 11:28:57 +00:00
|
|
|
setBlock(mutable, block);
|
2018-08-12 14:03:07 +00:00
|
|
|
} catch (MaxChangedBlocksException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (containsAny) {
|
|
|
|
changes++;
|
|
|
|
TaskManager.IMP.sync(new RunnableVal<Object>() {
|
|
|
|
@Override
|
|
|
|
public void run(Object value) {
|
2019-07-18 16:07:31 +00:00
|
|
|
regenerateChunk(cx, cz, biome, seed);
|
2018-08-12 14:03:07 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (changes != 0) {
|
|
|
|
flushQueue();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2010-10-02 21:52:42 +00:00
|
|
|
}
|