Change GeolyzerEvent.Scan to define a bounding box instead of a column. Closes #1149.

`geolyzer.scan` can now alternatively be called with `x, z, y, width, depth, height` as first arguments to define custom bounds (old parameters still work and will continue to return columns).
Layout of returned table will be such that `index = x + z*w + y*w*d`.
This commit is contained in:
Florian Nücke 2015-10-25 17:11:40 +01:00
parent d6838a9455
commit f46c22ba77
3 changed files with 82 additions and 24 deletions

View File

@ -36,18 +36,42 @@ public abstract class GeolyzerEvent extends Event {
* the geolyzer. By default this will yield a (noisy) listing of the * the geolyzer. By default this will yield a (noisy) listing of the
* hardness of the blocks. * hardness of the blocks.
* <p/> * <p/>
* Note: the y coordinate is computed as <tt>geolyzer.y - 32 + data.index</tt>. * The bounds are guaranteed to not define a volume larger than 64.
* Resulting data should be written to the {@link #data} array such that
* <code>index = x + z*w + y*w*d</code>, with <code>w = maxX - minX</code>
* and <code>d = maxZ - minZ</code> (<tt>h</tt> meaning height, <tt>d</tt>
* meaning depth).
*/ */
public static class Scan extends GeolyzerEvent { public static class Scan extends GeolyzerEvent {
/** /**
* The <em>relative</em> x coordinate of the column being scanned. * The <em>relative</em> minimal x coordinate of the box being scanned (inclusive).
*/ */
public final int scanX; public final int minX;
/** /**
* The <em>relative</em> z coordinate of the column being scanned. * The <em>relative</em> minimal y coordinate of the box being scanned (inclusive).
*/ */
public final int scanZ; public final int minY;
/**
* The <em>relative</em> minimal z coordinate of the box being scanned (inclusive).
*/
public final int minZ;
/**
* The <em>relative</em> maximal x coordinate of the box being scanned (inclusive).
*/
public final int maxX;
/**
* The <em>relative</em> maximal y coordinate of the box being scanned (inclusive).
*/
public final int maxY;
/**
* The <em>relative</em> maximal z coordinate of the box being scanned (inclusive).
*/
public final int maxZ;
/** /**
* The data for the column of blocks being scanned, which is an * The data for the column of blocks being scanned, which is an
@ -56,10 +80,14 @@ public abstract class GeolyzerEvent extends Event {
*/ */
public final float[] data = new float[64]; public final float[] data = new float[64];
public Scan(EnvironmentHost host, Map<?, ?> options, int scanX, int scanZ) { public Scan(EnvironmentHost host, Map<?, ?> options, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
super(host, options); super(host, options);
this.scanX = scanX; this.minX = minX;
this.scanZ = scanZ; this.minY = minY;
this.minZ = minZ;
this.maxX = maxX;
this.maxY = maxY;
this.maxZ = maxZ;
} }
} }

View File

@ -15,8 +15,6 @@ object GeolyzerHandler {
def onGeolyzerScan(e: GeolyzerEvent.Scan) { def onGeolyzerScan(e: GeolyzerEvent.Scan) {
val world = e.host.world val world = e.host.world
val blockPos = BlockPosition(e.host) val blockPos = BlockPosition(e.host)
val bx = blockPos.x + e.scanX
val bz = blockPos.z + e.scanZ
val includeReplaceable = e.options.get("includeReplaceable") match { val includeReplaceable = e.options.get("includeReplaceable") match {
case value: java.lang.Boolean => value.booleanValue() case value: java.lang.Boolean => value.booleanValue()
case _ => true case _ => true
@ -27,16 +25,25 @@ object GeolyzerHandler {
// Map to [-1, 1). The additional /33f is for normalization below. // Map to [-1, 1). The additional /33f is for normalization below.
noise.map(_ / 128f / 33f).copyToArray(e.data) noise.map(_ / 128f / 33f).copyToArray(e.data)
for (ry <- 0 until e.data.length) { val w = e.maxX - e.minX + 1
val by = blockPos.y + ry - 32 val d = e.maxZ - e.minZ + 1
if (!world.isAirBlock(bx, by, bz)) { for (ry <- e.minY to e.maxY; rz <- e.minZ to e.maxZ; rx <- e.minX to e.maxX) {
val block = world.getBlock(bx, by, bz) val x = blockPos.x + rx
val y = blockPos.y + ry
val z = blockPos.z + rz
val index = (rx - e.minX) + ((rz - e.minZ) + (ry - e.minY) * d) * w
if (world.blockExists(x, y, z) && !world.isAirBlock(x, y, z)) {
val block = world.getBlock(x, y, z)
if (block != null && (includeReplaceable || isFluid(block) || !block.isReplaceable(world, blockPos.x, blockPos.y, blockPos.z))) { if (block != null && (includeReplaceable || isFluid(block) || !block.isReplaceable(world, blockPos.x, blockPos.y, blockPos.z))) {
e.data(ry) = e.data(ry) * (math.abs(ry - 32) + 1) * Settings.get.geolyzerNoise + block.getBlockHardness(world, bx, by, bz) val dx = blockPos.x - x
val dy = blockPos.y - y
val dz = blockPos.z - z
val distance = math.sqrt(dx * dx + dy * dy + dz * dz).toFloat
e.data(index) = e.data(index) * distance * Settings.get.geolyzerNoise + block.getBlockHardness(world, x, y, z)
} }
else e.data(ry) = 0 else e.data(index) = 0
} }
else e.data(ry) = 0 else e.data(index) = 0
} }
} }

View File

@ -33,25 +33,48 @@ class Geolyzer(val host: EnvironmentHost) extends prefab.ManagedEnvironment {
withConnector(). withConnector().
create() create()
@Callback(doc = """function(x:number, z:number[, ignoreReplaceable:boolean|options:table]):table -- Analyzes the density of the column at the specified relative coordinates.""") @Callback(doc = """function(x:number, z:number[, y:number, w:number, d:number, h:number][, ignoreReplaceable:boolean|options:table]):table -- Analyzes the density of the column at the specified relative coordinates.""")
def scan(computer: Context, args: Arguments): Array[AnyRef] = { def scan(computer: Context, args: Arguments): Array[AnyRef] = {
val rx = args.checkInteger(0) val (minX, minY, minZ, maxX, maxY, maxZ, optIndex) = getScanArgs(args)
val rz = args.checkInteger(1) val volume = (maxX - minX + 1) * (maxZ - minZ + 1) * (maxY - minY + 1)
val options = if (args.isBoolean(2)) mapAsJavaMap(Map("includeReplaceable" -> !args.checkBoolean(2))) else args.optTable(2, Map.empty[AnyRef, AnyRef]) if (volume > 64) throw new IllegalArgumentException("volume too large (maximum is 64)")
val options = if (args.isBoolean(optIndex)) mapAsJavaMap(Map("includeReplaceable" -> !args.checkBoolean(optIndex))) else args.optTable(optIndex, Map.empty[AnyRef, AnyRef])
if (math.abs(rx) > Settings.get.geolyzerRange || math.abs(rz) > Settings.get.geolyzerRange) { if (math.abs(minX) > Settings.get.geolyzerRange || math.abs(maxX) > Settings.get.geolyzerRange ||
math.abs(minY) > Settings.get.geolyzerRange || math.abs(maxY) > Settings.get.geolyzerRange ||
math.abs(minZ) > Settings.get.geolyzerRange || math.abs(maxZ) > Settings.get.geolyzerRange) {
throw new IllegalArgumentException("location out of bounds") throw new IllegalArgumentException("location out of bounds")
} }
if (!node.tryChangeBuffer(-Settings.get.geolyzerScanCost)) if (!node.tryChangeBuffer(-Settings.get.geolyzerScanCost))
return result(Unit, "not enough energy") return result(Unit, "not enough energy")
val event = new GeolyzerEvent.Scan(host, options, rx, rz) val event = new GeolyzerEvent.Scan(host, options, minX, minY, minZ, maxX, maxY, maxZ)
MinecraftForge.EVENT_BUS.post(event) MinecraftForge.EVENT_BUS.post(event)
if (event.isCanceled) result(Unit, "scan was canceled") if (event.isCanceled) result(Unit, "scan was canceled")
else result(event.data) else result(event.data)
} }
private def getScanArgs(args: Arguments) = {
val minX = args.checkInteger(0)
val minZ = args.checkInteger(1)
if (args.isInteger(2) && args.isInteger(3) && args.isInteger(4) && args.isInteger(5)) {
val minY = args.checkInteger(2)
val w = args.checkInteger(3)
val d = args.checkInteger(4)
val h = args.checkInteger(5)
val maxX = minX + w - 1
val maxY = minY + h - 1
val maxZ = minZ + d - 1
(math.min(minX, maxX), math.min(minY, maxY), math.min(minZ, maxZ),
math.max(minX, maxX), math.max(minY, maxY), math.max(minZ, maxZ),
6)
}
else {
(minX, -32, minZ, minX, 31, minZ, 2)
}
}
@Callback(doc = """function(side:number[,options:table]):table -- Get some information on a directly adjacent block.""") @Callback(doc = """function(side:number[,options:table]):table -- Get some information on a directly adjacent block.""")
def analyze(computer: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { def analyze(computer: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
val side = args.checkSideAny(0) val side = args.checkSideAny(0)