@@ -1213,45 +1213,51 @@ class NPCImpl(
12131213 return false
12141214 }
12151215
1216- // For feet position, use collision shape to check if there's collision at the specific Y
1217- // NPC stands ON blocks, not IN them, so we need to check collision at the feet Y coordinate
1216+ // For feet position, check if there's collision at the feet Y
12181217 val feetBlockY = y.toInt()
12191218 val blockAtFeet = world.getBlockAt(x, feetBlockY, z)
12201219 val relativeY = y - feetBlockY // Y position relative to the block (0.0 to 1.0)
12211220
1221+ // If the block at feet level is passable (air), no collision - NPC can stand here
1222+ if (isPassable(blockAtFeet)) {
1223+ return true
1224+ }
1225+
1226+ // Block is not passable, check if NPC is standing on top of it using collision shape
12221227 try {
12231228 val collisionShape = blockAtFeet.collisionShape
12241229 if (collisionShape != null ) {
1225- // Create a small bounding box representing the NPC's feet at this Y position
1226- // NPC feet are roughly 0.6x0.6 at the center of the block
1227- val feetCheckBox = org.bukkit.util.BoundingBox (
1228- 0.2 , relativeY - 0.1 , 0.2 , // min (relative to block)
1229- 0.8 , relativeY + 0.1 , 0.8 // max (relative to block)
1230- )
1231-
1232- // If the collision shape overlaps at the feet position, there's collision
1233- // But if relativeY is at or above the top of the collision shape, it's fine (NPC stands ON it)
12341230 val boundingBoxes = collisionShape.boundingBoxes
12351231 if (boundingBoxes.isNotEmpty()) {
12361232 val maxBlockY = boundingBoxes.maxOfOrNull { it.maxY } ? : 1.0
12371233 // If feet Y is at or above the top of the collision shape, NPC can stand on it
1238- if (relativeY >= maxBlockY - 0.1 ) {
1239- return true // Standing on top of the block
1234+ if (relativeY >= maxBlockY - 0.15 ) {
1235+ return true // Standing on top of the block (with small tolerance)
12401236 }
1241- // Otherwise, check if there's collision
1237+ // Check if there's collision at the feet position
1238+ val feetCheckBox = org.bukkit.util.BoundingBox (
1239+ 0.2 , (relativeY - 0.15 ).coerceAtLeast(0.0 ), 0.2 , // min (relative to block)
1240+ 0.8 , (relativeY + 0.15 ).coerceAtMost(1.0 ), 0.8 // max (relative to block)
1241+ )
12421242 if (collisionShape.overlaps(feetCheckBox)) {
12431243 return false // Collision at feet
12441244 }
1245+ // No collision detected, allow it
1246+ return true
12451247 }
12461248 }
1249+ // No collision shape or empty, check if block is walkable (NPC might be standing on it)
1250+ if (isWalkable(blockAtFeet) && relativeY >= 0.3 ) {
1251+ return true // Standing on a walkable block
1252+ }
12471253 } catch (e: Exception ) {
1248- // Fallback: if block is passable, no collision
1249- // But if NPC is standing ON the block (relativeY > 0.5), allow it
1250- if (relativeY <= 0.5 && ! isPassable(blockAtFeet)) {
1251- return false
1254+ // Fallback: if relativeY is high enough (standing on top) or block is walkable, allow it
1255+ if (relativeY >= 0.3 || isWalkable(blockAtFeet)) {
1256+ return true
12521257 }
12531258 }
12541259
1260+ // Default: allow if no collision detected
12551261 return true
12561262 }
12571263
0 commit comments