2010-10-02 23:52:42 +02:00
|
|
|
// $Id$
|
|
|
|
/*
|
|
|
|
* WorldEdit
|
|
|
|
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.HashMap;
|
2010-10-05 09:40:50 +02:00
|
|
|
import java.util.HashSet;
|
2010-10-11 20:30:11 +02:00
|
|
|
import java.util.Stack;
|
2010-10-02 23:52:42 +02:00
|
|
|
import com.sk89q.worldedit.*;
|
|
|
|
|
|
|
|
/**
|
2010-10-05 09:40:50 +02:00
|
|
|
* This class can wrap all block editing operations into one "edit session" that
|
|
|
|
* stores the state of the blocks before modification. This allows for easy
|
|
|
|
* undo or redo. In addition to that, this class can use a "queue mode" that
|
|
|
|
* will know how to handle some special types of items such as signs and
|
|
|
|
* torches. For example, torches must be placed only after there is already
|
|
|
|
* a block below it, otherwise the torch will be placed as an item.
|
2010-10-02 23:52:42 +02:00
|
|
|
*
|
2010-10-05 09:40:50 +02:00
|
|
|
* @author sk89q
|
2010-10-02 23:52:42 +02:00
|
|
|
*/
|
|
|
|
public class EditSession {
|
|
|
|
/**
|
|
|
|
* Stores the original blocks before modification.
|
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
private HashMap<BlockVector,Integer> original = new HashMap<BlockVector,Integer>();
|
2010-10-05 01:39:35 +02:00
|
|
|
/**
|
|
|
|
* Stores the current blocks.
|
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
private HashMap<BlockVector,Integer> current = new HashMap<BlockVector,Integer>();
|
2010-10-05 09:40:50 +02:00
|
|
|
/**
|
|
|
|
* Queue.
|
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
private HashMap<BlockVector,Integer> queue = new HashMap<BlockVector,Integer>();
|
2010-10-05 01:39:35 +02:00
|
|
|
/**
|
|
|
|
* The maximum number of blocks to change at a time. If this number is
|
|
|
|
* exceeded, a MaxChangedBlocksException exception will be
|
|
|
|
* raised. -1 indicates no limit.
|
|
|
|
*/
|
|
|
|
private int maxBlocks = -1;
|
2010-10-05 09:40:50 +02:00
|
|
|
/**
|
|
|
|
* Indicates whether some types of blocks should be queued for best
|
|
|
|
* reproduction.
|
|
|
|
*/
|
|
|
|
private boolean queued = false;
|
|
|
|
/**
|
|
|
|
* List of object types to queue.
|
|
|
|
*/
|
2010-10-11 10:22:47 +02:00
|
|
|
private static final HashSet<Integer> queuedBlocks = new HashSet<Integer>();
|
2010-10-05 09:40:50 +02:00
|
|
|
|
|
|
|
static {
|
|
|
|
queuedBlocks.add(50); // Torch
|
|
|
|
queuedBlocks.add(37); // Yellow flower
|
|
|
|
queuedBlocks.add(38); // Red rose
|
|
|
|
queuedBlocks.add(39); // Brown mushroom
|
|
|
|
queuedBlocks.add(40); // Red mushroom
|
|
|
|
queuedBlocks.add(59); // Crops
|
|
|
|
queuedBlocks.add(63); // Sign
|
|
|
|
queuedBlocks.add(75); // Redstone torch (off)
|
|
|
|
queuedBlocks.add(76); // Redstone torch (on)
|
|
|
|
queuedBlocks.add(84); // Reed
|
|
|
|
}
|
2010-10-05 01:39:35 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor. There is no maximum blocks limit.
|
|
|
|
*/
|
|
|
|
public EditSession() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct the object with a maximum number of blocks.
|
|
|
|
*/
|
|
|
|
public EditSession(int maxBlocks) {
|
|
|
|
if (maxBlocks < -1) {
|
|
|
|
throw new IllegalArgumentException("Max blocks must be >= -1");
|
|
|
|
}
|
|
|
|
this.maxBlocks = maxBlocks;
|
|
|
|
}
|
2010-10-02 23:52:42 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets a block without changing history.
|
|
|
|
*
|
2010-10-11 10:22:47 +02:00
|
|
|
* @param pt
|
2010-10-02 23:52:42 +02:00
|
|
|
* @param blockType
|
|
|
|
* @return Whether the block changed
|
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
private boolean rawSetBlock(Vector pt, int blockType) {
|
2010-10-11 10:22:47 +02:00
|
|
|
return etc.getMCServer().e.d(pt.getBlockX(), pt.getBlockY(),
|
|
|
|
pt.getBlockZ(), blockType);
|
2010-10-02 23:52:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-10-05 09:40:50 +02:00
|
|
|
* Sets the block at position x, y, z with a block type. If queue mode is
|
|
|
|
* enabled, blocks may not be actually set in world until flushQueue()
|
|
|
|
* is called.
|
2010-10-02 23:52:42 +02:00
|
|
|
*
|
|
|
|
* @param x
|
|
|
|
* @param y
|
|
|
|
* @param z
|
|
|
|
* @param blockType
|
2010-10-05 09:40:50 +02:00
|
|
|
* @return Whether the block changed -- not entirely dependable
|
2010-10-02 23:52:42 +02:00
|
|
|
*/
|
2010-10-05 01:39:35 +02:00
|
|
|
public boolean setBlock(int x, int y, int z, int blockType)
|
|
|
|
throws MaxChangedBlocksException {
|
2010-10-13 03:03:56 +02:00
|
|
|
return setBlock(new Vector(x, y, z), blockType);
|
2010-10-11 10:22:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the block at position x, y, z with a block type. If queue mode is
|
|
|
|
* enabled, blocks may not be actually set in world until flushQueue()
|
|
|
|
* is called.
|
|
|
|
*
|
|
|
|
* @param pt
|
|
|
|
* @param blockType
|
|
|
|
* @return Whether the block changed -- not entirely dependable
|
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
public boolean setBlock(Vector pt, int blockType)
|
2010-10-11 10:22:47 +02:00
|
|
|
throws MaxChangedBlocksException {
|
2010-10-02 23:52:42 +02:00
|
|
|
if (!original.containsKey(pt)) {
|
2010-10-11 20:21:43 +02:00
|
|
|
original.put(pt.toBlockPoint(), getBlock(pt));
|
2010-10-05 01:39:35 +02:00
|
|
|
|
|
|
|
if (maxBlocks != -1 && original.size() > maxBlocks) {
|
|
|
|
throw new MaxChangedBlocksException(maxBlocks);
|
|
|
|
}
|
2010-10-02 23:52:42 +02:00
|
|
|
}
|
2010-10-11 10:22:47 +02:00
|
|
|
|
2010-10-11 20:21:43 +02:00
|
|
|
current.put(pt.toBlockPoint(), blockType);
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
return smartSetBlock(pt, blockType);
|
2010-10-05 09:40:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Actually set the block. Will use queue.
|
|
|
|
*
|
2010-10-11 10:22:47 +02:00
|
|
|
* @param pt
|
2010-10-05 09:40:50 +02:00
|
|
|
* @param blockType
|
|
|
|
* @return
|
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
private boolean smartSetBlock(Vector pt, int blockType) {
|
2010-10-05 09:40:50 +02:00
|
|
|
if (queued) {
|
|
|
|
if (blockType != 0 && queuedBlocks.contains(blockType)
|
2010-10-11 10:22:47 +02:00
|
|
|
&& rawGetBlock(pt.add(0, -1, 0)) == 0) {
|
2010-10-11 20:21:43 +02:00
|
|
|
queue.put(pt.toBlockPoint(), blockType);
|
2010-10-11 10:22:47 +02:00
|
|
|
return getBlock(pt) != blockType;
|
2010-10-05 09:40:50 +02:00
|
|
|
} else if (blockType == 0
|
2010-10-11 10:22:47 +02:00
|
|
|
&& queuedBlocks.contains(rawGetBlock(pt.add(0, 1, 0)))) {
|
|
|
|
rawSetBlock(pt.add(0, 1, 0), 0); // Prevent items from being dropped
|
2010-10-05 09:40:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-11 10:22:47 +02:00
|
|
|
return rawSetBlock(pt, blockType);
|
2010-10-02 23:52:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the block type at a position x, y, z.
|
|
|
|
*
|
2010-10-11 10:22:47 +02:00
|
|
|
* @param pt
|
2010-10-02 23:52:42 +02:00
|
|
|
* @return Block type
|
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
public int getBlock(Vector pt) {
|
2010-10-05 09:40:50 +02:00
|
|
|
// In the case of the queue, the block may have not actually been
|
|
|
|
// changed yet
|
|
|
|
if (queued) {
|
|
|
|
if (current.containsKey(pt)) {
|
|
|
|
return current.get(pt);
|
|
|
|
}
|
|
|
|
}
|
2010-10-11 10:22:47 +02:00
|
|
|
return etc.getMCServer().e.a(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
|
2010-10-05 09:40:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the block type at a position x, y, z.
|
|
|
|
*
|
|
|
|
* @param x
|
|
|
|
* @param y
|
|
|
|
* @param z
|
|
|
|
* @return Block type
|
|
|
|
*/
|
2010-10-11 10:22:47 +02:00
|
|
|
public int getBlock(int x, int y, int z) {
|
2010-10-13 03:03:56 +02:00
|
|
|
return getBlock(new Vector(x, y, z));
|
2010-10-11 10:22:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the block type at a position x, y, z.
|
|
|
|
*
|
|
|
|
* @param pt
|
|
|
|
* @return Block type
|
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
public int rawGetBlock(Vector pt) {
|
2010-10-11 10:22:47 +02:00
|
|
|
return etc.getMCServer().e.a(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
|
2010-10-02 23:52:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Restores all blocks to their initial state.
|
|
|
|
*/
|
|
|
|
public void undo() {
|
2010-10-13 03:03:56 +02:00
|
|
|
for (Map.Entry<BlockVector,Integer> entry : original.entrySet()) {
|
|
|
|
BlockVector pt = (BlockVector)entry.getKey();
|
2010-10-11 10:22:47 +02:00
|
|
|
smartSetBlock(pt, (int)entry.getValue());
|
2010-10-02 23:52:42 +02:00
|
|
|
}
|
2010-10-05 09:40:50 +02:00
|
|
|
flushQueue();
|
2010-10-02 23:52:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets to new state.
|
|
|
|
*/
|
|
|
|
public void redo() {
|
2010-10-13 03:03:56 +02:00
|
|
|
for (Map.Entry<BlockVector,Integer> entry : current.entrySet()) {
|
|
|
|
BlockVector pt = (BlockVector)entry.getKey();
|
2010-10-11 10:22:47 +02:00
|
|
|
smartSetBlock(pt, (int)entry.getValue());
|
2010-10-02 23:52:42 +02:00
|
|
|
}
|
2010-10-05 09:40:50 +02:00
|
|
|
flushQueue();
|
2010-10-02 23:52:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the number of changed blocks.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public int size() {
|
|
|
|
return original.size();
|
|
|
|
}
|
2010-10-05 01:39:35 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the maximum number of blocks that can be changed. -1 will be
|
|
|
|
* returned if disabled.
|
|
|
|
*
|
2010-10-11 17:56:19 +02:00
|
|
|
* @return block change limit
|
2010-10-05 01:39:35 +02:00
|
|
|
*/
|
|
|
|
public int getBlockChangeLimit() {
|
|
|
|
return maxBlocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the maximum number of blocks that can be changed.
|
|
|
|
*
|
|
|
|
* @param maxBlocks -1 to disable
|
|
|
|
*/
|
|
|
|
public void setBlockChangeLimit(int maxBlocks) {
|
|
|
|
if (maxBlocks < -1) {
|
|
|
|
throw new IllegalArgumentException("Max blocks must be >= -1");
|
|
|
|
}
|
|
|
|
this.maxBlocks = maxBlocks;
|
|
|
|
}
|
2010-10-05 09:40:50 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns queue status.
|
|
|
|
*
|
2010-10-11 17:56:19 +02:00
|
|
|
* @return whether the queue is enabled
|
2010-10-05 09:40:50 +02:00
|
|
|
*/
|
|
|
|
public boolean isQueueEnabled() {
|
|
|
|
return queued;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Queue certain types of block for better reproduction of those blocks.
|
|
|
|
*/
|
|
|
|
public void enableQueue() {
|
|
|
|
queued = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disable the queue. This will flush the queue.
|
|
|
|
*/
|
|
|
|
public void disableQueue() {
|
|
|
|
if (queued != false) {
|
|
|
|
flushQueue();
|
|
|
|
}
|
|
|
|
queued = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finish off the queue.
|
|
|
|
*/
|
|
|
|
public void flushQueue() {
|
|
|
|
if (!queued) { return; }
|
|
|
|
|
2010-10-13 03:03:56 +02:00
|
|
|
for (Map.Entry<BlockVector,Integer> entry : queue.entrySet()) {
|
|
|
|
BlockVector pt = (BlockVector)entry.getKey();
|
2010-10-11 10:22:47 +02:00
|
|
|
rawSetBlock(pt, (int)entry.getValue());
|
2010-10-05 09:40:50 +02:00
|
|
|
}
|
|
|
|
}
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Fills an area recursively in the X/Z directions.
|
|
|
|
*
|
|
|
|
* @param x
|
|
|
|
* @param z
|
|
|
|
* @param origin
|
|
|
|
* @param blockType
|
|
|
|
* @param radius
|
|
|
|
* @param depth
|
2010-10-11 17:56:19 +02:00
|
|
|
* @return number of blocks affected
|
2010-10-11 10:22:47 +02:00
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
public int fillXZ(int x, int z, Vector origin, int blockType, int radius, int depth)
|
2010-10-11 10:22:47 +02:00
|
|
|
throws MaxChangedBlocksException {
|
|
|
|
double dist = Math.sqrt(Math.pow(origin.getX() - x, 2) + Math.pow(origin.getZ() - z, 2));
|
|
|
|
int minY = origin.getBlockY() - depth + 1;
|
|
|
|
int affected = 0;
|
|
|
|
|
|
|
|
if (dist > radius) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-10-13 03:03:56 +02:00
|
|
|
if (getBlock(new Vector(x, origin.getY(), z)) == 0) {
|
2010-10-11 10:22:47 +02:00
|
|
|
affected = fillY(x, (int)origin.getY(), z, blockType, minY);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
affected += fillXZ(x + 1, z, origin, blockType, radius, depth);
|
|
|
|
affected += fillXZ(x - 1, z, origin, blockType, radius, depth);
|
|
|
|
affected += fillXZ(x, z + 1, origin, blockType, radius, depth);
|
|
|
|
affected += fillXZ(x, z - 1, origin, blockType, radius, depth);
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recursively fills a block and below until it hits another block.
|
|
|
|
*
|
|
|
|
* @param x
|
|
|
|
* @param cy
|
|
|
|
* @param z
|
|
|
|
* @param blockType
|
|
|
|
* @param minY
|
|
|
|
* @throws MaxChangedBlocksException
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
private int fillY(int x, int cy, int z, int blockType, int minY)
|
|
|
|
throws MaxChangedBlocksException {
|
|
|
|
int affected = 0;
|
|
|
|
|
|
|
|
for (int y = cy; y >= minY; y--) {
|
2010-10-13 03:03:56 +02:00
|
|
|
Vector pt = new Vector(x, y, z);
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
if (getBlock(pt) == 0) {
|
|
|
|
setBlock(pt, blockType);
|
|
|
|
affected++;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove blocks above.
|
|
|
|
*
|
|
|
|
* @param pos
|
2010-10-11 17:56:19 +02:00
|
|
|
* @param size
|
2010-10-11 10:22:47 +02:00
|
|
|
* @param height
|
2010-10-11 17:56:19 +02:00
|
|
|
* @return number of blocks affected
|
2010-10-11 10:22:47 +02:00
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
public int removeAbove(Vector pos, int size, int height) throws
|
2010-10-11 10:22:47 +02:00
|
|
|
MaxChangedBlocksException {
|
|
|
|
int maxY = Math.min(127, pos.getBlockY() + height - 1);
|
|
|
|
size--;
|
|
|
|
int affected = 0;
|
|
|
|
|
|
|
|
for (int x = (int)pos.getX() - size; x <= (int)pos.getX() + size; x++) {
|
|
|
|
for (int z = (int)pos.getZ() - size; z <= (int)pos.getZ() + size; z++) {
|
|
|
|
for (int y = (int)pos.getY(); y <= maxY; y++) {
|
2010-10-13 03:03:56 +02:00
|
|
|
Vector pt = new Vector(x, y, z);
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
if (getBlock(pt) != 0) {
|
|
|
|
setBlock(pt, 0);
|
|
|
|
affected++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove blocks below.
|
|
|
|
*
|
|
|
|
* @param pos
|
2010-10-11 17:56:19 +02:00
|
|
|
* @param size
|
2010-10-11 10:22:47 +02:00
|
|
|
* @param height
|
2010-10-11 17:56:19 +02:00
|
|
|
* @return number of blocks affected
|
2010-10-11 10:22:47 +02:00
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
public int removeBelow(Vector pos, int size, int height) throws
|
2010-10-11 10:22:47 +02:00
|
|
|
MaxChangedBlocksException {
|
|
|
|
int minY = Math.max(0, pos.getBlockY() - height);
|
|
|
|
size--;
|
|
|
|
int affected = 0;
|
|
|
|
|
|
|
|
for (int x = (int)pos.getX() - size; x <= (int)pos.getX() + size; x++) {
|
|
|
|
for (int z = (int)pos.getZ() - size; z <= (int)pos.getZ() + size; z++) {
|
|
|
|
for (int y = (int)pos.getY(); y >= minY; y--) {
|
2010-10-13 03:03:56 +02:00
|
|
|
Vector pt = new Vector(x, y, z);
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
if (getBlock(pt) != 0) {
|
|
|
|
setBlock(pt, 0);
|
|
|
|
affected++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets all the blocks inside a region to a certain block type.
|
|
|
|
*
|
|
|
|
* @param region
|
|
|
|
* @param blockType
|
2010-10-11 17:56:19 +02:00
|
|
|
* @return number of blocks affected
|
2010-10-11 10:22:47 +02:00
|
|
|
* @throws MaxChangedBlocksException
|
|
|
|
*/
|
|
|
|
public int setBlocks(Region region, int blockType)
|
|
|
|
throws MaxChangedBlocksException {
|
|
|
|
int affected = 0;
|
|
|
|
|
|
|
|
if (region instanceof CuboidRegion) {
|
|
|
|
// Doing this for speed
|
2010-10-13 03:03:56 +02:00
|
|
|
Vector min = region.getMinimumPoint();
|
|
|
|
Vector max = region.getMaximumPoint();
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
|
|
|
|
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
|
|
|
|
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
|
2010-10-13 03:03:56 +02:00
|
|
|
Vector pt = new Vector(x, y, z);
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
if (setBlock(pt, blockType)) {
|
|
|
|
affected++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2010-10-13 03:03:56 +02:00
|
|
|
for (Vector pt : region) {
|
2010-10-11 10:22:47 +02:00
|
|
|
if (setBlock(pt, blockType)) {
|
|
|
|
affected++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces all the blocks of a type inside a region to another block type.
|
|
|
|
*
|
|
|
|
* @param region
|
|
|
|
* @param fromBlockType -1 for non-air
|
|
|
|
* @param toBlockType
|
2010-10-11 17:56:19 +02:00
|
|
|
* @return number of blocks affected
|
2010-10-11 10:22:47 +02:00
|
|
|
* @throws MaxChangedBlocksException
|
|
|
|
*/
|
|
|
|
public int replaceBlocks(Region region, int fromBlockType, int toBlockType)
|
|
|
|
throws MaxChangedBlocksException {
|
|
|
|
int affected = 0;
|
|
|
|
|
|
|
|
if (region instanceof CuboidRegion) {
|
|
|
|
// Doing this for speed
|
2010-10-13 03:03:56 +02:00
|
|
|
Vector min = region.getMinimumPoint();
|
|
|
|
Vector max = region.getMaximumPoint();
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
|
|
|
|
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
|
|
|
|
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
|
2010-10-13 03:03:56 +02:00
|
|
|
Vector pt = new Vector(x, y, z);
|
2010-10-11 10:22:47 +02:00
|
|
|
int curBlockType = getBlock(pt);
|
|
|
|
|
|
|
|
if (fromBlockType == -1 && curBlockType != 0 ||
|
|
|
|
curBlockType == fromBlockType) {
|
|
|
|
if (setBlock(pt, toBlockType)) {
|
|
|
|
affected++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2010-10-13 03:03:56 +02:00
|
|
|
for (Vector pt : region) {
|
2010-10-11 10:22:47 +02:00
|
|
|
int curBlockType = getBlock(pt);
|
|
|
|
|
|
|
|
if (fromBlockType == -1 && curBlockType != 0 ||
|
|
|
|
curBlockType == fromBlockType) {
|
|
|
|
if (setBlock(pt, toBlockType)) {
|
|
|
|
affected++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make faces of the region (as if it was a cuboid if it's not).
|
|
|
|
*
|
|
|
|
* @param region
|
|
|
|
* @param blockType
|
2010-10-11 17:56:19 +02:00
|
|
|
* @return number of blocks affected
|
2010-10-11 10:22:47 +02:00
|
|
|
* @throws MaxChangedBlocksException
|
|
|
|
*/
|
|
|
|
public int makeCuboidFaces(Region region, int blockType)
|
|
|
|
throws MaxChangedBlocksException {
|
|
|
|
int affected = 0;
|
|
|
|
|
2010-10-13 03:03:56 +02:00
|
|
|
Vector min = region.getMinimumPoint();
|
|
|
|
Vector max = region.getMaximumPoint();
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
|
|
|
|
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
|
|
|
|
if (setBlock(x, y, min.getBlockZ(), blockType)) { affected++; }
|
|
|
|
if (setBlock(x, y, max.getBlockZ(), blockType)) { affected++; }
|
|
|
|
affected++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
|
|
|
|
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
|
|
|
|
if (setBlock(min.getBlockX(), y, z, blockType)) { affected++; }
|
|
|
|
if (setBlock(max.getBlockX(), y, z, blockType)) { affected++; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
|
|
|
|
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
|
|
|
|
if (setBlock(x, min.getBlockY(), z, blockType)) { affected++; }
|
|
|
|
if (setBlock(x, max.getBlockY(), z, blockType)) { affected++; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Overlays a layer of blocks over a cuboid area.
|
|
|
|
*
|
|
|
|
* @param region
|
|
|
|
* @param blockType
|
2010-10-11 17:56:19 +02:00
|
|
|
* @return number of blocks affected
|
2010-10-11 10:22:47 +02:00
|
|
|
* @throws MaxChangedBlocksException
|
|
|
|
*/
|
|
|
|
public int overlayCuboidBlocks(Region region, int blockType)
|
|
|
|
throws MaxChangedBlocksException {
|
2010-10-13 03:03:56 +02:00
|
|
|
Vector min = region.getMinimumPoint();
|
|
|
|
Vector max = region.getMaximumPoint();
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
int upperY = Math.min(127, max.getBlockY() + 1);
|
|
|
|
int lowerY = Math.max(0, min.getBlockY()- 1);
|
|
|
|
|
|
|
|
int affected = 0;
|
|
|
|
|
|
|
|
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
|
|
|
|
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
|
|
|
|
for (int y = upperY; y >= lowerY; y--) {
|
|
|
|
if (y + 1 <= 127 && getBlock(x, y, z) != 0 && getBlock(x, y + 1, z) == 0) {
|
|
|
|
if (setBlock(x, y + 1, z, blockType)) {
|
|
|
|
affected++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
|
|
|
|
2010-10-11 19:27:18 +02:00
|
|
|
/**
|
|
|
|
* Stack a cuboid region.
|
|
|
|
*
|
|
|
|
* @param region
|
|
|
|
* @param xm
|
|
|
|
* @param ym
|
|
|
|
* @param zm
|
|
|
|
* @param count
|
|
|
|
* @param copyAir
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException
|
|
|
|
*/
|
2010-10-11 10:22:47 +02:00
|
|
|
public int stackCuboidRegion(Region region, int xm, int ym, int zm,
|
|
|
|
int count, boolean copyAir)
|
|
|
|
throws MaxChangedBlocksException {
|
|
|
|
int affected = 0;
|
|
|
|
|
2010-10-13 03:03:56 +02:00
|
|
|
Vector min = region.getMinimumPoint();
|
|
|
|
Vector max = region.getMaximumPoint();
|
2010-10-11 10:22:47 +02:00
|
|
|
int xs = region.getWidth();
|
|
|
|
int ys = region.getHeight();
|
|
|
|
int zs = region.getLength();
|
|
|
|
|
|
|
|
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
|
|
|
|
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
|
|
|
|
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
|
2010-10-13 03:03:56 +02:00
|
|
|
int blockType = getBlock(new Vector(x, y, z));
|
2010-10-11 10:22:47 +02:00
|
|
|
|
|
|
|
if (blockType != 0 || copyAir) {
|
|
|
|
for (int i = 1; i <= count; i++) {
|
|
|
|
if (setBlock(x + xs * xm * i, y + ys * ym * i,
|
|
|
|
z + zs * zm * i, blockType)) {
|
|
|
|
affected++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
2010-10-11 20:17:32 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Drain nearby pools of water or lava.
|
|
|
|
*
|
|
|
|
* @param pos
|
|
|
|
* @param radius
|
|
|
|
* @return number of blocks affected
|
|
|
|
* @throws MaxChangedBlocksException
|
|
|
|
*/
|
2010-10-13 03:03:56 +02:00
|
|
|
public int drainArea(Vector pos, int radius) throws MaxChangedBlocksException {
|
2010-10-11 20:17:32 +02:00
|
|
|
int affected = 0;
|
|
|
|
|
2010-10-13 03:03:56 +02:00
|
|
|
HashSet<BlockVector> visited = new HashSet<BlockVector>();
|
|
|
|
Stack<BlockVector> queue = new Stack<BlockVector>();
|
2010-10-11 20:17:32 +02:00
|
|
|
|
|
|
|
for (int x = pos.getBlockX() - 1; x <= pos.getBlockX() + 1; x++) {
|
|
|
|
for (int z = pos.getBlockZ() - 1; z <= pos.getBlockZ() + 1; z++) {
|
|
|
|
for (int y = pos.getBlockY() - 1; y <= pos.getBlockY() + 1; y++) {
|
2010-10-13 03:03:56 +02:00
|
|
|
queue.push(new BlockVector(x, y, z));
|
2010-10-11 20:17:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-11 20:30:11 +02:00
|
|
|
while (!queue.empty()) {
|
2010-10-13 03:03:56 +02:00
|
|
|
BlockVector cur = queue.pop();
|
2010-10-11 20:30:11 +02:00
|
|
|
|
|
|
|
int type = getBlock(cur);
|
2010-10-11 20:17:32 +02:00
|
|
|
|
2010-10-11 20:30:11 +02:00
|
|
|
// Check block type
|
|
|
|
if (type != 8 && type != 9 && type != 10 && type != 11) {
|
|
|
|
continue;
|
|
|
|
}
|
2010-10-11 20:17:32 +02:00
|
|
|
|
2010-10-11 20:30:11 +02:00
|
|
|
// Don't want to revisit
|
|
|
|
if (visited.contains(cur)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
visited.add(cur);
|
2010-10-11 20:17:32 +02:00
|
|
|
|
2010-10-11 20:30:11 +02:00
|
|
|
// Check radius
|
|
|
|
if (pos.distance(cur) > radius) {
|
|
|
|
continue;
|
|
|
|
}
|
2010-10-11 20:17:32 +02:00
|
|
|
|
2010-10-11 20:30:11 +02:00
|
|
|
for (int x = cur.getBlockX() - 1; x <= cur.getBlockX() + 1; x++) {
|
|
|
|
for (int z = cur.getBlockZ() - 1; z <= cur.getBlockZ() + 1; z++) {
|
|
|
|
for (int y = cur.getBlockY() - 1; y <= cur.getBlockY() + 1; y++) {
|
2010-10-13 03:03:56 +02:00
|
|
|
BlockVector newPos = new BlockVector(x, y, z);
|
2010-10-11 20:17:32 +02:00
|
|
|
|
2010-10-11 20:30:11 +02:00
|
|
|
if (!cur.equals(newPos)) {
|
|
|
|
queue.push(newPos);
|
|
|
|
}
|
2010-10-11 20:17:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-11 20:30:11 +02:00
|
|
|
if (setBlock(cur, 0)) {
|
|
|
|
affected++;
|
|
|
|
}
|
2010-10-11 20:17:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
2010-10-02 23:52:42 +02:00
|
|
|
}
|