geforkt von Mirrors/FastAsyncWorldEdit
Merge pull request #269 from sk89q/generatebiome
Added a //generatebiome command, which uses formulae to generate free-form biomes.
Dieser Commit ist enthalten in:
Commit
9930fb7338
@ -46,6 +46,7 @@ import com.sk89q.worldedit.patterns.Pattern;
|
|||||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
import com.sk89q.worldedit.regions.RegionOperationException;
|
import com.sk89q.worldedit.regions.RegionOperationException;
|
||||||
|
import com.sk89q.worldedit.shape.ArbitraryBiomeShape;
|
||||||
import com.sk89q.worldedit.shape.ArbitraryShape;
|
import com.sk89q.worldedit.shape.ArbitraryShape;
|
||||||
import com.sk89q.worldedit.shape.RegionShape;
|
import com.sk89q.worldedit.shape.RegionShape;
|
||||||
import com.sk89q.worldedit.shape.WorldEditExpressionEnvironment;
|
import com.sk89q.worldedit.shape.WorldEditExpressionEnvironment;
|
||||||
@ -3019,4 +3020,38 @@ public class EditSession {
|
|||||||
} // while
|
} // while
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int makeBiomeShape(final Region region, final Vector zero, final Vector unit, final BiomeType biomeType, final String expressionString, final boolean hollow) throws ExpressionException, MaxChangedBlocksException {
|
||||||
|
final Vector2D zero2D = zero.toVector2D();
|
||||||
|
final Vector2D unit2D = unit.toVector2D();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
final ArbitraryBiomeShape shape = new ArbitraryBiomeShape(region) {
|
||||||
|
@Override
|
||||||
|
protected BiomeType getBiome(int x, int z, BiomeType defaultBiomeType) {
|
||||||
|
final Vector2D current = new Vector2D(x, z);
|
||||||
|
environment.setCurrentBlock(current.toVector(0));
|
||||||
|
final Vector2D scaled = current.subtract(zero2D).divide(unit2D);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (expression.evaluate(scaled.getX(), scaled.getZ()) <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Allow biome setting via a script variable (needs BiomeType<->int mapping)
|
||||||
|
return defaultBiomeType;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return shape.generate(this, biomeType, hollow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,12 @@ import com.sk89q.minecraft.util.commands.Command;
|
|||||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||||
import com.sk89q.minecraft.util.commands.CommandPermissions;
|
import com.sk89q.minecraft.util.commands.CommandPermissions;
|
||||||
import com.sk89q.minecraft.util.commands.Logging;
|
import com.sk89q.minecraft.util.commands.Logging;
|
||||||
|
import com.sk89q.worldedit.BiomeType;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.LocalPlayer;
|
import com.sk89q.worldedit.LocalPlayer;
|
||||||
import com.sk89q.worldedit.LocalSession;
|
import com.sk89q.worldedit.LocalSession;
|
||||||
import com.sk89q.worldedit.Vector;
|
import com.sk89q.worldedit.Vector;
|
||||||
|
import com.sk89q.worldedit.Vector2D;
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
import com.sk89q.worldedit.expression.ExpressionException;
|
import com.sk89q.worldedit.expression.ExpressionException;
|
||||||
@ -415,4 +417,71 @@ public class GenerationCommands {
|
|||||||
player.printError(e.getMessage());
|
player.printError(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
aliases = { "/generatebiome", "/genbiome", "/gb" },
|
||||||
|
usage = "<block> <expression>",
|
||||||
|
desc = "Sets biome according to a formula.",
|
||||||
|
help =
|
||||||
|
"Generates a shape according to a formula that is expected to\n" +
|
||||||
|
"return positive numbers (true) if the point is inside the shape\n" +
|
||||||
|
"Optionally set type/data to the desired block.\n" +
|
||||||
|
"Flags:\n" +
|
||||||
|
" -h to generate a hollow shape\n" +
|
||||||
|
" -r to use raw minecraft coordinates\n" +
|
||||||
|
" -o is like -r, except offset from placement.\n" +
|
||||||
|
" -c is like -r, except offset selection center.\n" +
|
||||||
|
"If neither -r nor -o is given, the selection is mapped to -1..1\n" +
|
||||||
|
"See also tinyurl.com/wesyntax.",
|
||||||
|
flags = "hroc",
|
||||||
|
min = 2,
|
||||||
|
max = -1
|
||||||
|
)
|
||||||
|
@CommandPermissions({"worldedit.generation.shape", "worldedit.biome.set"})
|
||||||
|
@Logging(ALL)
|
||||||
|
public void generateBiome(CommandContext args, LocalSession session, LocalPlayer player,
|
||||||
|
EditSession editSession) throws WorldEditException {
|
||||||
|
|
||||||
|
final BiomeType target = we.getServer().getBiomes().get(args.getString(0));
|
||||||
|
final Region region = session.getSelection(player.getWorld());
|
||||||
|
|
||||||
|
final boolean hollow = args.hasFlag('h');
|
||||||
|
|
||||||
|
final String expression = args.getJoinedStrings(1);
|
||||||
|
|
||||||
|
final Vector zero;
|
||||||
|
Vector unit;
|
||||||
|
|
||||||
|
if (args.hasFlag('r')) {
|
||||||
|
zero = Vector.ZERO;
|
||||||
|
unit = Vector.ONE;
|
||||||
|
} else if (args.hasFlag('o')) {
|
||||||
|
zero = session.getPlacementPosition(player);
|
||||||
|
unit = Vector.ONE;
|
||||||
|
} else if (args.hasFlag('c')) {
|
||||||
|
final Vector min = region.getMinimumPoint();
|
||||||
|
final Vector max = region.getMaximumPoint();
|
||||||
|
|
||||||
|
zero = max.add(min).multiply(0.5);
|
||||||
|
unit = Vector.ONE;
|
||||||
|
} else {
|
||||||
|
final Vector min = region.getMinimumPoint();
|
||||||
|
final Vector max = region.getMaximumPoint();
|
||||||
|
|
||||||
|
zero = max.add(min).multiply(0.5);
|
||||||
|
unit = max.subtract(zero);
|
||||||
|
|
||||||
|
if (unit.getX() == 0) unit = unit.setX(1.0);
|
||||||
|
if (unit.getY() == 0) unit = unit.setY(1.0);
|
||||||
|
if (unit.getZ() == 0) unit = unit.setZ(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final int affected = editSession.makeBiomeShape(region, zero, unit, target, expression, hollow);
|
||||||
|
player.findFreePosition();
|
||||||
|
player.print("Biome changed to " + target.getName() + ". " + affected + " columns affected.");
|
||||||
|
} catch (ExpressionException e) {
|
||||||
|
player.printError(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
189
src/main/java/com/sk89q/worldedit/shape/ArbitraryBiomeShape.java
Normale Datei
189
src/main/java/com/sk89q/worldedit/shape/ArbitraryBiomeShape.java
Normale Datei
@ -0,0 +1,189 @@
|
|||||||
|
// $Id$
|
||||||
|
/*
|
||||||
|
* WorldEdit
|
||||||
|
* Copyright (C) 2010 sk89q <http://www.sk89q.com> and contributors
|
||||||
|
*
|
||||||
|
* 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.shape;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.BiomeType;
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.Vector2D;
|
||||||
|
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||||
|
import com.sk89q.worldedit.regions.FlatRegion;
|
||||||
|
import com.sk89q.worldedit.regions.Region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates solid and hollow shapes according to materials returned by the
|
||||||
|
* {@link #getBiome} method.
|
||||||
|
*
|
||||||
|
* @author TomyLobo
|
||||||
|
*/
|
||||||
|
public abstract class ArbitraryBiomeShape {
|
||||||
|
private final FlatRegion extent;
|
||||||
|
private int cacheOffsetX;
|
||||||
|
private int cacheOffsetZ;
|
||||||
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
|
private int cacheSizeX;
|
||||||
|
private int cacheSizeZ;
|
||||||
|
|
||||||
|
public ArbitraryBiomeShape(Region extent) {
|
||||||
|
if (extent instanceof FlatRegion) {
|
||||||
|
this.extent = (FlatRegion) extent;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO: polygonize
|
||||||
|
this.extent = new CuboidRegion(extent.getWorld(), extent.getMinimumPoint(), extent.getMaximumPoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2D min = extent.getMinimumPoint().toVector2D();
|
||||||
|
Vector2D max = extent.getMaximumPoint().toVector2D();
|
||||||
|
|
||||||
|
cacheOffsetX = min.getBlockX() - 1;
|
||||||
|
cacheOffsetZ = min.getBlockZ() - 1;
|
||||||
|
|
||||||
|
cacheSizeX = (int) (max.getX() - cacheOffsetX + 2);
|
||||||
|
cacheSizeZ = (int) (max.getZ() - cacheOffsetZ + 2);
|
||||||
|
|
||||||
|
cache = new BiomeType[cacheSizeX * cacheSizeZ];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Iterable<Vector2D> getExtent() {
|
||||||
|
return extent.asFlatRegion();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache entries:
|
||||||
|
* null = unknown
|
||||||
|
* OUTSIDE = outside
|
||||||
|
* else = inside
|
||||||
|
*/
|
||||||
|
private final BiomeType[] cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this function to specify the shape to generate.
|
||||||
|
*
|
||||||
|
* @param x X coordinate to be queried
|
||||||
|
* @param z Z coordinate to be queried
|
||||||
|
* @param defaultBiomeType The default biome for the current column.
|
||||||
|
* @return material to place or null to not place anything.
|
||||||
|
*/
|
||||||
|
protected abstract BiomeType getBiome(int x, int z, BiomeType defaultBiomeType);
|
||||||
|
|
||||||
|
private BiomeType getBiomeCached(int x, int z, BiomeType biomeType) {
|
||||||
|
final int index = (z - cacheOffsetZ) + (x - cacheOffsetX) * cacheSizeZ;
|
||||||
|
|
||||||
|
final BiomeType cacheEntry = cache[index];
|
||||||
|
if (cacheEntry == null) {// unknown, fetch material
|
||||||
|
final BiomeType material = getBiome(x, z, biomeType);
|
||||||
|
if (material == null) {
|
||||||
|
// outside
|
||||||
|
cache[index] = OUTSIDE;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache[index] = material;
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheEntry == OUTSIDE) {
|
||||||
|
// outside
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInsideCached(int x, int z, BiomeType biomeType) {
|
||||||
|
final int index = (z - cacheOffsetZ) + (x - cacheOffsetX) * cacheSizeZ;
|
||||||
|
|
||||||
|
final BiomeType cacheEntry = cache[index];
|
||||||
|
if (cacheEntry == null) {
|
||||||
|
// unknown block, meaning they must be outside the extent at this stage, but might still be inside the shape
|
||||||
|
return getBiomeCached(x, z, biomeType) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheEntry != OUTSIDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the shape.
|
||||||
|
*
|
||||||
|
* @param editSession The EditSession to use.
|
||||||
|
* @param biomeType The default biome type.
|
||||||
|
* @param hollow Specifies whether to generate a hollow shape.
|
||||||
|
* @return number of affected blocks.
|
||||||
|
*/
|
||||||
|
public int generate(EditSession editSession, BiomeType biomeType, boolean hollow) {
|
||||||
|
int affected = 0;
|
||||||
|
|
||||||
|
for (Vector2D position : getExtent()) {
|
||||||
|
int x = position.getBlockX();
|
||||||
|
int z = position.getBlockZ();
|
||||||
|
|
||||||
|
if (!hollow) {
|
||||||
|
final BiomeType material = getBiome(x, z, biomeType);
|
||||||
|
if (material != OUTSIDE) {
|
||||||
|
editSession.getWorld().setBiome(position, material);
|
||||||
|
++affected;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final BiomeType material = getBiomeCached(x, z, biomeType);
|
||||||
|
if (material == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean draw = false;
|
||||||
|
do {
|
||||||
|
if (!isInsideCached(x + 1, z, biomeType)) {
|
||||||
|
draw = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!isInsideCached(x - 1, z, biomeType)) {
|
||||||
|
draw = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!isInsideCached(x, z + 1, biomeType)) {
|
||||||
|
draw = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!isInsideCached(x, z - 1, biomeType)) {
|
||||||
|
draw = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
if (!draw) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
editSession.getWorld().setBiome(position, material);
|
||||||
|
++affected;
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final BiomeType OUTSIDE = new BiomeType() {
|
||||||
|
public String getName() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren