diff --git a/src/main/kotlin/com/github/kaaariyaaa/elevator/listeners/EventListener.kt b/src/main/kotlin/com/github/kaaariyaaa/elevator/listeners/EventListener.kt index f1a7182..bdfdd25 100644 --- a/src/main/kotlin/com/github/kaaariyaaa/elevator/listeners/EventListener.kt +++ b/src/main/kotlin/com/github/kaaariyaaa/elevator/listeners/EventListener.kt @@ -7,6 +7,8 @@ import com.destroystokyo.paper.event.player.PlayerJumpEvent import org.bukkit.entity.Player import org.bukkit.Material import org.bukkit.block.Block +import org.bukkit.Location +import org.bukkit.util.BoundingBox import com.github.kaaariyaaa.elevator.App @@ -23,20 +25,26 @@ class EventListener(private val plugin: App) : Listener { val blockUnder = player.location.subtract(0.0, 1.0, 0.0).block if (blockUnder.type.isSolid && blockUnder.type in oreBlock) { - var location = blockUnder.location - location = location.add(0.0, 1.0, 0.0) + var location = blockUnder.location.clone() + location.add(0.0, 1.0, 0.0) val maxHeight = plugin.config.getInt("maxHeight", 64) var distance = 0 - while (!(location.block.type in oreBlock) && distance < maxHeight) { - location = location.add(0.0, 1.0, 0.0) + while (distance < maxHeight) { + if (location.block.type in oreBlock) { + val feet = location.clone().add(0.0, 1.0, 0.0) + if (isSafe(player, feet)) { + val dest = location.clone().add(0.5, 1.0, 0.5) + dest.yaw = player.location.yaw + dest.pitch = player.location.pitch + player.teleport(dest) + return + } + } + location.add(0.0, 1.0, 0.0) distance++ } - - if (location.block.type in oreBlock) { - player.teleport(location.add(0.5, 1.0, 0.5).addRotation(player.location.yaw, player.location.pitch)) - } } } @@ -48,20 +56,83 @@ class EventListener(private val plugin: App) : Listener { val blockUnder = player.location.subtract(0.0, 1.0, 0.0).block if (blockUnder.type in oreBlock) { - var location = blockUnder.location - location = location.subtract(0.0, 1.0, 0.0) + var location = blockUnder.location.clone() + location.subtract(0.0, 1.0, 0.0) val maxHeight = plugin.config.getInt("maxHeight", 64) var distance = 0 - while (!(location.block.type in oreBlock) && distance < maxHeight) { - location = location.subtract(0.0, 1.0, 0.0) + while (distance < maxHeight) { + if (location.block.type in oreBlock) { + val feet = location.clone().add(0.0, 1.0, 0.0) + if (isSafe(player, feet)) { + val dest = location.clone().add(0.5, 1.0, 0.5) + dest.yaw = player.location.yaw + dest.pitch = player.location.pitch + player.teleport(dest) + return + } + } + location.subtract(0.0, 1.0, 0.0) distance++ } - - if (location.block.type in oreBlock) { - player.teleport(location.add(0.5, 1.0, 0.5).addRotation(player.location.yaw, player.location.pitch)) - } } } + + private fun isSafe(player: Player, feetLocation: Location): Boolean { + val x = feetLocation.blockX + 0.5 + val y = feetLocation.blockY.toDouble() + val z = feetLocation.blockZ + 0.5 + + val playerBox = player.boundingBox + val halfW = playerBox.widthX / 2.0 + val halfD = playerBox.widthZ / 2.0 + val h = playerBox.height + + // 足元の座標からプレイヤーの高さ分のBoundingBoxを作成し、全身(上半身含む)の衝突判定を行う + val targetBox = org.bukkit.util.BoundingBox(x - halfW, y, z - halfD, x + halfW, y + h, z + halfD) + val world = feetLocation.world + + val minX = kotlin.math.floor(targetBox.minX).toInt() + val maxX = kotlin.math.ceil(targetBox.maxX).toInt() + val minY = kotlin.math.floor(targetBox.minY).toInt() + val maxY = kotlin.math.ceil(targetBox.maxY).toInt() + val minZ = kotlin.math.floor(targetBox.minZ).toInt() + val maxZ = kotlin.math.ceil(targetBox.maxZ).toInt() + + for (bx in minX..maxX) { + for (by in minY..maxY) { + for (bz in minZ..maxZ) { + val block = world.getBlockAt(bx, by, bz) + if (block.isEmpty) continue + + val voxelShape = block.collisionShape + for (box in voxelShape.boundingBoxes as Collection) { + val absBox = box.clone().shift(bx.toDouble(), by.toDouble(), bz.toDouble()) + + // 先に交差判定を行う + if (!targetBox.overlaps(absBox)) continue + + // カーペット(高さ <= 0.0625)や開いた/下付きトラップドアのような軽微な衝突は無視する + // 物体が大きく遮っている場合はテレポートを阻止する + // カーペットの高さは0.0625。下付きトラップドアは通常0.1875。 + // 足元の障害物が最小限であればテレポートを許可する。 + + val intersection = targetBox.clone().intersection(absBox) + val intHeight = intersection.height + + // 交差が非常に小さい場合(カーペットに触れている程度など)は許可する + // 足元にあり、高さが0.2未満(トラップドアは約0.1875)であれば許容する + val isAtFeetLevel = (absBox.minY - y) < 0.1 + if (isAtFeetLevel && absBox.height < 0.2) { + continue + } + + return false + } + } + } + } + return true + } }