Skip to content

Commit a31c8df

Browse files
committed
improved entity spleef block position detection
1 parent ecbd48c commit a31c8df

File tree

3 files changed

+91
-10
lines changed

3 files changed

+91
-10
lines changed

src/main/kotlin/com/lambda/interaction/construction/processing/ProcessorRegistry.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ object ProcessorRegistry : Loadable {
106106

107107
override fun load() = "Loaded ${processors.size} pre processors"
108108

109-
fun TargetState.getProcessingInfo(pos: BlockPos) =
109+
fun TargetState.getProcessingInfo(pos: BlockPos): PreProcessingInfo? =
110110
if (this !is TargetState.State) PreProcessingInfo.DEFAULT
111111
else {
112112
val get: () -> PreProcessingInfo? = get@{
@@ -115,9 +115,8 @@ object ProcessorRegistry : Loadable {
115115
processors.forEach { processor ->
116116
if (!processor.acceptsState(blockState)) return@forEach
117117
processor.preProcess(blockState, pos, infoAccumulator)
118-
if (infoAccumulator.shouldBeOmitted) {
118+
if (infoAccumulator.shouldBeOmitted)
119119
return@get null
120-
}
121120
}
122121

123122
infoAccumulator.complete()

src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PlaceChecker.kt

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ import com.lambda.interaction.construction.result.Dependable
2424
import com.lambda.interaction.construction.result.results.GenericResult
2525
import com.lambda.interaction.construction.result.results.PlaceResult
2626
import com.lambda.interaction.construction.simulation.ISimInfo
27+
import com.lambda.interaction.construction.simulation.ISimInfo.Companion.simInfo
2728
import com.lambda.interaction.construction.simulation.SimChecker
2829
import com.lambda.interaction.construction.simulation.SimCheckerDsl
2930
import com.lambda.interaction.construction.simulation.SimInfo
31+
import com.lambda.interaction.construction.simulation.checks.BreakChecker.Companion.checkBreaks
3032
import com.lambda.interaction.construction.simulation.checks.PlaceChecker.RotatePlaceTest.Companion.rotatePlaceTest
3133
import com.lambda.interaction.construction.verify.TargetState
3234
import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer
@@ -43,8 +45,10 @@ import com.lambda.interaction.request.rotating.visibilty.lookAt
4345
import com.lambda.interaction.request.rotating.visibilty.lookInDirection
4446
import com.lambda.util.BlockUtils
4547
import com.lambda.util.BlockUtils.blockState
48+
import com.lambda.util.EntityUtils.getPositionsWithinHitboxXZ
4649
import com.lambda.util.item.ItemStackUtils.inventoryIndex
4750
import com.lambda.util.item.ItemUtils.blockItem
51+
import com.lambda.util.math.MathUtils.floorToInt
4852
import com.lambda.util.math.minus
4953
import com.lambda.util.player.MovementUtils.sneaking
5054
import com.lambda.util.player.copyPlayer
@@ -54,8 +58,10 @@ import kotlinx.coroutines.cancel
5458
import kotlinx.coroutines.launch
5559
import kotlinx.coroutines.supervisorScope
5660
import net.minecraft.block.BlockState
61+
import net.minecraft.block.ShapeContext
5762
import net.minecraft.block.pattern.CachedBlockPosition
5863
import net.minecraft.client.network.ClientPlayerEntity
64+
import net.minecraft.entity.Entity
5965
import net.minecraft.item.ItemPlacementContext
6066
import net.minecraft.item.ItemStack
6167
import net.minecraft.util.Hand
@@ -141,7 +147,7 @@ class PlaceChecker @SimCheckerDsl private constructor(simInfo: SimInfo)
141147
}
142148
}
143149

144-
private fun AutomatedSafeContext.selectHitPos(
150+
private suspend fun AutomatedSafeContext.selectHitPos(
145151
validHits: Collection<CheckedHit>,
146152
fakePlayer: ClientPlayerEntity,
147153
swapStack: ItemStack
@@ -203,7 +209,7 @@ class PlaceChecker @SimCheckerDsl private constructor(simInfo: SimInfo)
203209
return
204210
}
205211

206-
private fun AutomatedSafeContext.simRotatePlace(
212+
private suspend fun AutomatedSafeContext.simRotatePlace(
207213
fakePlayer: ClientPlayerEntity,
208214
checkedHit: CheckedHit,
209215
context: ItemPlacementContext
@@ -231,18 +237,53 @@ class PlaceChecker @SimCheckerDsl private constructor(simInfo: SimInfo)
231237
return null
232238
}
233239

234-
private fun AutomatedSafeContext.testPlaceState(context: ItemPlacementContext): PlaceTest {
235-
val resultState = context.stack.blockItem.getPlacementState(context) ?: run {
236-
result(PlaceResult.BlockedByEntity(pos, emptyList()))
237-
return PlaceTest(state, PlaceTestResult.BlockedByEntity)
238-
}
240+
private suspend fun AutomatedSafeContext.testPlaceState(context: ItemPlacementContext): PlaceTest {
241+
val resultState = context.stack.blockItem.getPlacementState(context)
242+
?: run {
243+
val blockingEntities = handleEntityBlockage(context)
244+
result(PlaceResult.BlockedByEntity(pos, blockingEntities))
245+
return PlaceTest(state, PlaceTestResult.BlockedByEntity)
246+
}
239247

240248
return if (!targetState.matches(resultState, pos, preProcessing.ignore)) {
241249
result(PlaceResult.NoIntegrity(pos, resultState, context, (targetState as? TargetState.State)?.blockState))
242250
PlaceTest(resultState, PlaceTestResult.NoIntegrity)
243251
} else PlaceTest(resultState, PlaceTestResult.Success)
244252
}
245253

254+
private suspend fun AutomatedSafeContext.handleEntityBlockage(context: ItemPlacementContext): List<Entity> {
255+
val theoreticalState = context.stack.blockItem.block.getPlacementState(context)
256+
?: return emptyList()
257+
258+
val collisionShape = theoreticalState.getCollisionShape(
259+
world,
260+
context.blockPos,
261+
ShapeContext.ofPlacement(player)
262+
).offset(context.blockPos)
263+
264+
val collidingEntities = collisionShape.boundingBoxes.flatMap { box ->
265+
world.entities.filter { it.boundingBox.intersects(box) }
266+
}
267+
268+
if (collidingEntities.isNotEmpty()) {
269+
collidingEntities
270+
.mapNotNull { entity ->
271+
val hitbox = entity.boundingBox
272+
entity.getPositionsWithinHitboxXZ(
273+
(pos.y - (hitbox.maxY - hitbox.minY)).floorToInt(),
274+
pos.y
275+
)
276+
}
277+
.flatten()
278+
.forEach { support ->
279+
simInfo(support, blockState(support), TargetState.Empty)?.checkBreaks()
280+
}
281+
result(PlaceResult.BlockedByEntity(pos, collidingEntities))
282+
}
283+
284+
return collidingEntities
285+
}
286+
246287
private class RotatePlaceTest private constructor(
247288
val resultState: BlockState,
248289
val currentDirIsValid: Boolean,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.util
19+
20+
import com.lambda.util.math.MathUtils.floorToInt
21+
import net.minecraft.entity.Entity
22+
import net.minecraft.util.math.BlockPos
23+
24+
object EntityUtils {
25+
fun Entity.getPositionsWithinHitboxXZ(minY: Int, maxY: Int): Set<BlockPos> {
26+
val hitbox = boundingBox
27+
val minX = hitbox.minX.floorToInt()
28+
val maxX = hitbox.maxX.floorToInt()
29+
val minZ = hitbox.minZ.floorToInt()
30+
val maxZ = hitbox.maxZ.floorToInt()
31+
val positions = mutableSetOf<BlockPos>()
32+
(minX..maxX).forEach { x ->
33+
(minY..maxY).forEach { y ->
34+
(minZ..maxZ).forEach { z ->
35+
positions.add(BlockPos(x, y, z))
36+
}
37+
}
38+
}
39+
return positions
40+
}
41+
}

0 commit comments

Comments
 (0)