From 1279537f8d92f4f7993862bccb7228cdec6169a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 26 Jun 2016 11:21:59 +0200 Subject: [PATCH] Added custom intersect check for racks to take into account the mountables being inset by one. Avoid floating point inaccuracies leading to incorrect local coordinates for mountable onActivate. --- .../scala/li/cil/oc/common/block/Rack.scala | 56 +++++++++++++++++-- .../li/cil/oc/common/tileentity/Rack.scala | 7 ++- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/block/Rack.scala b/src/main/scala/li/cil/oc/common/block/Rack.scala index 5e40c1985..ebda20c40 100644 --- a/src/main/scala/li/cil/oc/common/block/Rack.scala +++ b/src/main/scala/li/cil/oc/common/block/Rack.scala @@ -9,7 +9,9 @@ import li.cil.oc.common.GuiType import li.cil.oc.common.tileentity import net.minecraft.client.renderer.texture.IIconRegister import net.minecraft.entity.player.EntityPlayer +import net.minecraft.util.AxisAlignedBB import net.minecraft.util.IIcon +import net.minecraft.util.MovingObjectPosition import net.minecraft.util.Vec3 import net.minecraft.world.IBlockAccess import net.minecraft.world.World @@ -71,22 +73,64 @@ class Rack extends RedstoneAware with traits.SpecialBlock with traits.PowerAccep // ----------------------------------------------------------------------- // + final val collisionBounds = Array( + AxisAlignedBB.getBoundingBox(0, 0, 0, 1, 1/16f, 1), + AxisAlignedBB.getBoundingBox(0, 15/16f, 0, 1, 1, 1), + AxisAlignedBB.getBoundingBox(0, 0, 0, 1, 1, 1/16f), + AxisAlignedBB.getBoundingBox(0, 0, 15/16f, 1, 1, 1), + AxisAlignedBB.getBoundingBox(0, 0, 0, 1/16f, 1, 1), + AxisAlignedBB.getBoundingBox(15/16f, 0, 0, 1, 1, 1), + AxisAlignedBB.getBoundingBox(1/16f, 1/16f, 1/16f, 15/16f, 15/16f, 15/16f) + ) + + override protected def intersect(world: World, x: Int, y: Int, z: Int, start: Vec3, end: Vec3): MovingObjectPosition = { + world.getTileEntity(x, y, z) match { + case rack: tileentity.Rack => + var closestDistance = Double.PositiveInfinity + var closest: Option[MovingObjectPosition] = None + + def intersect(bounds: AxisAlignedBB): Unit = { + val hit = bounds.copy().offset(x, y, z).calculateIntercept(start, end) + if (hit != null) { + val distance = hit.hitVec.distanceTo(start) + if (distance < closestDistance) { + closestDistance = distance + closest = Option(hit) + } + } + } + val facings = ForgeDirection.VALID_DIRECTIONS + for (i <- 0 until facings.length) { + if (rack.facing != facings(i)) { + intersect(collisionBounds(i)) + } + } + intersect(collisionBounds.last) + closest.map(hit => new MovingObjectPosition(x, y, z, hit.sideHit, hit.hitVec)).orNull + case _ => super.intersect(world, x, y, z, start, end) + } + } + override def onBlockActivated(world: World, x: Int, y: Int, z: Int, player: EntityPlayer, side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float): Boolean = { world.getTileEntity(x, y, z) match { case rack: tileentity.Rack => rack.slotAt(side, hitX, hitY, hitZ) match { case Some(slot) => - val hitVec = Vec3.createVectorHelper(hitX, hitY, hitZ) + // Snap to grid to get same behavior on client and server... + val hitVec = Vec3.createVectorHelper((hitX*16f).toInt/16f, (hitY*16f).toInt/16f, (hitZ*16f).toInt/16f) val rotation = side match { case ForgeDirection.WEST => Math.toRadians(90).toFloat case ForgeDirection.NORTH => Math.toRadians(180).toFloat case ForgeDirection.EAST => Math.toRadians(270).toFloat case _ => 0 } - val localHitVec = rotate(hitVec.addVector(-0.5, -0.5, -0.5), rotation).addVector(0.5, 0.5, 0.5) - localHitVec.xCoord = ((if (side.offsetX != 0) 1 - localHitVec.xCoord else localHitVec.xCoord) * 16 - 1) / 14f - localHitVec.yCoord = ((1 - localHitVec.yCoord) * 16 - 2 - 3 * slot) / 3f - rack.getMountable(slot) match { - case mountable: RackMountable if mountable.onActivate(player, localHitVec.xCoord.toFloat, localHitVec.yCoord.toFloat) => return true // Activation handled by mountable. + // Rotate *centers* of pixels to keep association when reversing axis. + val localHitVec = rotate(hitVec.addVector(-0.5+1/32f, -0.5+1/32f, -0.5+1/32f), rotation).addVector(0.5-1/32f, 0.5-1/32f, 0.5-1/32f) + val globalX = (localHitVec.xCoord * 16.05f).toInt // [0, 15], work around floating point inaccuracies + val globalY = (localHitVec.yCoord * 16.05f).toInt // [0, 15], work around floating point inaccuracies + val localX = (if (side.offsetX != 0) 15 - globalX else globalX) - 1 + val localY = (15 - globalY) - 2 - 3 * slot + if (localX >= 0 && localX < 14 && localY >= 0 && localY < 3) rack.getMountable(slot) match { + case mountable: RackMountable if mountable.onActivate(player, localX / 14f, localY / 3f) => return true // Activation handled by mountable. case _ => } case _ => diff --git a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala index c7ba53cb4..2d1465e08 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala @@ -444,9 +444,10 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance def slotAt(side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = { if (side == facing) { - val l = 2 / 16.0 - val h = 14 / 16.0 - val slot = (((1 - hitY) - l) / (h - l) * getSizeInventory).toInt + val globalY = (hitY * 16).toInt // [0, 15] + val l = 2 + val h = 14 + val slot = ((15 - globalY) - l) * getSizeInventory / (h - l) Some(math.max(0, math.min(getSizeInventory - 1, slot))) } else None