2011-09-25 03:17:48 +00:00
|
|
|
// $Id$
|
|
|
|
/*
|
|
|
|
* WorldEdit
|
2012-01-05 21:38:23 +00:00
|
|
|
* Copyright (C) 2010 sk89q <http://www.sk89q.com> and contributors
|
2011-09-25 03:17:48 +00:00
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.sk89q.worldedit;
|
|
|
|
|
2011-10-31 21:03:35 +00:00
|
|
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
2011-09-25 03:17:48 +00:00
|
|
|
import com.sk89q.worldedit.patterns.Pattern;
|
|
|
|
import com.sk89q.worldedit.regions.Region;
|
|
|
|
|
2011-11-24 03:06:14 +00:00
|
|
|
/**
|
|
|
|
* Generates solid and hollow shapes according to materials returned by the
|
|
|
|
* {@link #getMaterial} method.
|
|
|
|
*
|
|
|
|
* @author TomyLobo
|
|
|
|
*/
|
2011-09-25 03:17:48 +00:00
|
|
|
public abstract class ArbitraryShape {
|
2011-10-31 21:03:35 +00:00
|
|
|
private final Region extent;
|
2011-11-24 09:11:53 +00:00
|
|
|
private int cacheOffsetX;
|
|
|
|
private int cacheOffsetY;
|
|
|
|
private int cacheOffsetZ;
|
2011-10-31 21:03:35 +00:00
|
|
|
private int cacheSizeX;
|
|
|
|
private int cacheSizeY;
|
|
|
|
private int cacheSizeZ;
|
2011-09-25 03:17:48 +00:00
|
|
|
|
|
|
|
public ArbitraryShape(Region extent) {
|
|
|
|
this.extent = extent;
|
2011-10-31 21:03:35 +00:00
|
|
|
|
|
|
|
Vector min = extent.getMinimumPoint();
|
|
|
|
Vector max = extent.getMaximumPoint();
|
|
|
|
|
2011-11-24 09:11:53 +00:00
|
|
|
cacheOffsetX = min.getBlockX() - 1;
|
|
|
|
cacheOffsetY = min.getBlockY() - 1;
|
|
|
|
cacheOffsetZ = min.getBlockZ() - 1;
|
2011-10-31 21:03:35 +00:00
|
|
|
|
2011-11-24 09:11:53 +00:00
|
|
|
cacheSizeX = (int) (max.getX() - cacheOffsetX + 2);
|
|
|
|
cacheSizeY = (int) (max.getY() - cacheOffsetY + 2);
|
|
|
|
cacheSizeZ = (int) (max.getZ() - cacheOffsetZ + 2);
|
2011-10-31 21:03:35 +00:00
|
|
|
|
|
|
|
cache = new short[cacheSizeX * cacheSizeY * cacheSizeZ];
|
2011-09-25 03:17:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected Region getExtent() {
|
|
|
|
return extent;
|
|
|
|
}
|
|
|
|
|
2011-10-31 21:03:35 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Cache entries:
|
|
|
|
* 0 = unknown
|
|
|
|
* -1 = outside
|
|
|
|
* -2 = inside but type and data 0
|
2011-11-24 09:11:53 +00:00
|
|
|
* > 0 = inside, value = (type | (data << 8)), not handling data < 0
|
2011-10-31 21:03:35 +00:00
|
|
|
*/
|
|
|
|
private final short[] cache;
|
|
|
|
|
2011-11-24 03:06:14 +00:00
|
|
|
/**
|
|
|
|
* Override this function to specify the shape to generate.
|
|
|
|
*
|
|
|
|
* @param x
|
|
|
|
* @param y
|
|
|
|
* @param z
|
|
|
|
* @param defaultMaterial The material returned by the pattern for the current block.
|
|
|
|
* @return material to place or null to not place anything.
|
|
|
|
*/
|
2011-10-31 21:03:35 +00:00
|
|
|
protected abstract BaseBlock getMaterial(int x, int y, int z, BaseBlock defaultMaterial);
|
|
|
|
|
|
|
|
private BaseBlock getMaterialCached(int x, int y, int z, Pattern pattern) {
|
2011-11-24 09:11:53 +00:00
|
|
|
final int index = (y - cacheOffsetY) + (z - cacheOffsetZ) * cacheSizeY + (x - cacheOffsetX) * cacheSizeY * cacheSizeZ;
|
2011-10-31 21:03:35 +00:00
|
|
|
|
|
|
|
final short cacheEntry = cache[index];
|
|
|
|
switch (cacheEntry) {
|
|
|
|
case 0:
|
|
|
|
// unknown, fetch material
|
|
|
|
final BaseBlock material = getMaterial(x, y, z, pattern.next(new BlockVector(x, y, z)));
|
|
|
|
if (material == null) {
|
|
|
|
// outside
|
|
|
|
cache[index] = -1;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2011-11-23 01:29:48 +00:00
|
|
|
short newCacheEntry = (short) (material.getType() | ((material.getData() + 1) << 8));
|
2011-10-31 21:03:35 +00:00
|
|
|
if (newCacheEntry == 0) {
|
|
|
|
// type and data 0
|
|
|
|
newCacheEntry = -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
cache[index] = newCacheEntry;
|
|
|
|
return material;
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
// outside
|
|
|
|
return null;
|
|
|
|
|
|
|
|
case -2:
|
|
|
|
// type and data 0
|
|
|
|
return new BaseBlock(0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new BaseBlock(cacheEntry & 255, ((cacheEntry >> 8) - 1) & 15);
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isInsideCached(int x, int y, int z, Pattern pattern) {
|
2011-11-24 09:11:53 +00:00
|
|
|
final int index = (y - cacheOffsetY) + (z - cacheOffsetZ) * cacheSizeY + (x - cacheOffsetX) * cacheSizeY * cacheSizeZ;
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-10-31 21:03:35 +00:00
|
|
|
switch (cache[index]) {
|
|
|
|
case 0:
|
|
|
|
// unknown block, meaning they must be outside the extent at this stage, but might still be inside the shape
|
|
|
|
return getMaterialCached(x, y, z, pattern) != null;
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
// outside
|
|
|
|
return false;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// inside
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2011-09-25 03:17:48 +00:00
|
|
|
|
2011-11-24 03:06:14 +00:00
|
|
|
/**
|
|
|
|
* Generates the shape.
|
|
|
|
*
|
|
|
|
* @param editSession
|
|
|
|
* @param pattern The pattern to generate default materials from.
|
|
|
|
* @param hollow Specifies whether to generate a hollow shape.
|
|
|
|
* @return number of affected blocks.
|
|
|
|
* @throws MaxChangedBlocksException
|
|
|
|
*/
|
2011-09-25 03:17:48 +00:00
|
|
|
public int generate(EditSession editSession, Pattern pattern, boolean hollow) throws MaxChangedBlocksException {
|
|
|
|
int affected = 0;
|
|
|
|
|
|
|
|
for (BlockVector position : getExtent()) {
|
2011-10-31 21:03:35 +00:00
|
|
|
int x = position.getBlockX();
|
|
|
|
int y = position.getBlockY();
|
|
|
|
int z = position.getBlockZ();
|
|
|
|
|
|
|
|
if (!hollow) {
|
|
|
|
final BaseBlock material = getMaterial(x, y, z, pattern.next(position));
|
|
|
|
if (material != null && editSession.setBlock(position, material)) {
|
|
|
|
++affected;
|
|
|
|
}
|
2011-09-25 03:17:48 +00:00
|
|
|
|
2011-10-31 21:03:35 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
final BaseBlock material = getMaterialCached(x, y, z, pattern);
|
|
|
|
if (material == null) {
|
|
|
|
continue;
|
|
|
|
}
|
2011-09-25 03:17:48 +00:00
|
|
|
|
|
|
|
if (hollow) {
|
|
|
|
boolean draw = false;
|
|
|
|
do {
|
2011-10-31 21:03:35 +00:00
|
|
|
if (!isInsideCached(x + 1, y, z, pattern)) {
|
2011-09-25 03:17:48 +00:00
|
|
|
draw = true;
|
|
|
|
break;
|
|
|
|
}
|
2011-10-31 21:03:35 +00:00
|
|
|
if (!isInsideCached(x - 1, y, z, pattern)) {
|
2011-09-25 03:17:48 +00:00
|
|
|
draw = true;
|
|
|
|
break;
|
|
|
|
}
|
2011-10-31 21:03:35 +00:00
|
|
|
if (!isInsideCached(x, y + 1, z, pattern)) {
|
2011-09-25 03:17:48 +00:00
|
|
|
draw = true;
|
|
|
|
break;
|
|
|
|
}
|
2011-10-31 21:03:35 +00:00
|
|
|
if (!isInsideCached(x, y - 1, z, pattern)) {
|
2011-09-25 03:17:48 +00:00
|
|
|
draw = true;
|
|
|
|
break;
|
|
|
|
}
|
2011-10-31 21:03:35 +00:00
|
|
|
if (!isInsideCached(x, y, z + 1, pattern)) {
|
2011-09-25 03:17:48 +00:00
|
|
|
draw = true;
|
|
|
|
break;
|
|
|
|
}
|
2011-10-31 21:03:35 +00:00
|
|
|
if (!isInsideCached(x, y, z - 1, pattern)) {
|
2011-09-25 03:17:48 +00:00
|
|
|
draw = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (false);
|
|
|
|
|
|
|
|
if (!draw) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-31 21:03:35 +00:00
|
|
|
if (editSession.setBlock(position, material)) {
|
2011-09-25 03:17:48 +00:00
|
|
|
++affected;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return affected;
|
|
|
|
}
|
|
|
|
}
|