From f46c22ba77ec89a2230e4d776af3804d91b646e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 25 Oct 2015 17:11:40 +0100 Subject: [PATCH] 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`. --- .../li/cil/oc/api/event/GeolyzerEvent.java | 44 +++++++++++++++---- .../cil/oc/common/event/GeolyzerHandler.scala | 25 +++++++---- .../li/cil/oc/server/component/Geolyzer.scala | 37 +++++++++++++--- 3 files changed, 82 insertions(+), 24 deletions(-) diff --git a/src/main/java/li/cil/oc/api/event/GeolyzerEvent.java b/src/main/java/li/cil/oc/api/event/GeolyzerEvent.java index 52c9ab02a..2d7d0f4be 100644 --- a/src/main/java/li/cil/oc/api/event/GeolyzerEvent.java +++ b/src/main/java/li/cil/oc/api/event/GeolyzerEvent.java @@ -36,18 +36,42 @@ public abstract class GeolyzerEvent extends Event { * the geolyzer. By default this will yield a (noisy) listing of the * hardness of the blocks. *

- * Note: the y coordinate is computed as geolyzer.y - 32 + data.index. + * The bounds are guaranteed to not define a volume larger than 64. + * Resulting data should be written to the {@link #data} array such that + * index = x + z*w + y*w*d, with w = maxX - minX + * and d = maxZ - minZ (h meaning height, d + * meaning depth). */ public static class Scan extends GeolyzerEvent { /** - * The relative x coordinate of the column being scanned. + * The relative minimal x coordinate of the box being scanned (inclusive). */ - public final int scanX; + public final int minX; /** - * The relative z coordinate of the column being scanned. + * The relative minimal y coordinate of the box being scanned (inclusive). */ - public final int scanZ; + public final int minY; + + /** + * The relative minimal z coordinate of the box being scanned (inclusive). + */ + public final int minZ; + + /** + * The relative maximal x coordinate of the box being scanned (inclusive). + */ + public final int maxX; + + /** + * The relative maximal y coordinate of the box being scanned (inclusive). + */ + public final int maxY; + + /** + * The relative 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 @@ -56,10 +80,14 @@ public abstract class GeolyzerEvent extends Event { */ 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); - this.scanX = scanX; - this.scanZ = scanZ; + this.minX = minX; + this.minY = minY; + this.minZ = minZ; + this.maxX = maxX; + this.maxY = maxY; + this.maxZ = maxZ; } } diff --git a/src/main/scala/li/cil/oc/common/event/GeolyzerHandler.scala b/src/main/scala/li/cil/oc/common/event/GeolyzerHandler.scala index 948afffc3..0505083e5 100644 --- a/src/main/scala/li/cil/oc/common/event/GeolyzerHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/GeolyzerHandler.scala @@ -15,8 +15,6 @@ object GeolyzerHandler { def onGeolyzerScan(e: GeolyzerEvent.Scan) { val world = e.host.world val blockPos = BlockPosition(e.host) - val bx = blockPos.x + e.scanX - val bz = blockPos.z + e.scanZ val includeReplaceable = e.options.get("includeReplaceable") match { case value: java.lang.Boolean => value.booleanValue() case _ => true @@ -27,16 +25,25 @@ object GeolyzerHandler { // Map to [-1, 1). The additional /33f is for normalization below. noise.map(_ / 128f / 33f).copyToArray(e.data) - for (ry <- 0 until e.data.length) { - val by = blockPos.y + ry - 32 - if (!world.isAirBlock(bx, by, bz)) { - val block = world.getBlock(bx, by, bz) + val w = e.maxX - e.minX + 1 + val d = e.maxZ - e.minZ + 1 + for (ry <- e.minY to e.maxY; rz <- e.minZ to e.maxZ; rx <- e.minX to e.maxX) { + 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))) { - 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 } } diff --git a/src/main/scala/li/cil/oc/server/component/Geolyzer.scala b/src/main/scala/li/cil/oc/server/component/Geolyzer.scala index 4ea1e0a2c..38befe3fc 100644 --- a/src/main/scala/li/cil/oc/server/component/Geolyzer.scala +++ b/src/main/scala/li/cil/oc/server/component/Geolyzer.scala @@ -33,25 +33,48 @@ class Geolyzer(val host: EnvironmentHost) extends prefab.ManagedEnvironment { withConnector(). 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] = { - val rx = args.checkInteger(0) - val rz = args.checkInteger(1) - val options = if (args.isBoolean(2)) mapAsJavaMap(Map("includeReplaceable" -> !args.checkBoolean(2))) else args.optTable(2, Map.empty[AnyRef, AnyRef]) - - if (math.abs(rx) > Settings.get.geolyzerRange || math.abs(rz) > Settings.get.geolyzerRange) { + val (minX, minY, minZ, maxX, maxY, maxZ, optIndex) = getScanArgs(args) + val volume = (maxX - minX + 1) * (maxZ - minZ + 1) * (maxY - minY + 1) + 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(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") } if (!node.tryChangeBuffer(-Settings.get.geolyzerScanCost)) 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) if (event.isCanceled) result(Unit, "scan was canceled") 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.""") def analyze(computer: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { val side = args.checkSideAny(0)