diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/EnderTeleporterFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/EnderTeleporterFeature.kt index 58100f781..16b46c622 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/EnderTeleporterFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/EnderTeleporterFeature.kt @@ -12,15 +12,15 @@ import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerPlayer import net.minecraft.sounds.SoundEvents import net.minecraft.sounds.SoundSource +import net.minecraft.tags.BlockTags import net.minecraft.world.level.ClipContext import net.minecraft.world.level.block.Block -import net.minecraft.world.level.material.Material import net.minecraft.world.phys.HitResult +import net.minecraft.world.phys.shapes.BooleanOp import net.minecraft.world.phys.shapes.CollisionContext +import net.minecraft.world.phys.shapes.Shapes import net.minecraftforge.client.event.RenderLevelStageEvent import net.minecraftforge.event.entity.living.LivingDeathEvent -import org.lwjgl.opengl.GL11.GL_ALWAYS -import org.lwjgl.opengl.GL11.GL_LESS import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.ServerConfig @@ -29,7 +29,6 @@ import ru.dbotthepony.mc.otm.android.AndroidResearchManager import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability import ru.dbotthepony.mc.otm.capability.extractEnergyInnerExact import ru.dbotthepony.mc.otm.capability.matteryPlayer -import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.render.DynamicBufferSource import ru.dbotthepony.mc.otm.client.render.ResearchIcons import ru.dbotthepony.mc.otm.client.render.element @@ -42,6 +41,7 @@ import ru.dbotthepony.mc.otm.core.component2 import ru.dbotthepony.mc.otm.core.component3 import ru.dbotthepony.mc.otm.core.formatPower import ru.dbotthepony.mc.otm.core.genericPositions +import ru.dbotthepony.mc.otm.core.minus import ru.dbotthepony.mc.otm.core.plus import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.shortestDistanceBetween @@ -74,23 +74,43 @@ class EnderTeleporterFeature(capability: MatteryPlayerCapability) : AndroidActiv cooldown = nbt.getInt("cooldown") } + private fun isValidGround(blockPos: BlockPos): Boolean { + return canSupportPlayer(blockPos) && !isWall(blockPos) + } + + private fun canSupportPlayer(blockPos: BlockPos): Boolean { + val state = ply.level.getBlockState(blockPos) + + if (state.`is`(BlockTags.CLIMBABLE)) { + return true // ladders can always support player + } + + val shape = state.getCollisionShape(ply.level, blockPos, CollisionContext.of(ply)) + return Shapes.joinIsNotEmpty(shape, SHAPE_CHECK_SUPPORT_PLAYER, BooleanOp.AND) + } + + private fun isWall(blockPos: BlockPos): Boolean { + val shape = ply.level.getBlockState(blockPos).getCollisionShape(ply.level, blockPos, CollisionContext.of(ply)) + return Shapes.joinIsNotEmpty(shape, SHAPE_CHECK_NOT_FENCE, BooleanOp.AND) + } + + private fun isAirGap(blockPos: BlockPos): Boolean { + val state = ply.level.getBlockState(blockPos) + + if (state.isAir) { + return true + } + + val shape = state.getCollisionShape(ply.level, blockPos, CollisionContext.of(ply)) + return shape.isEmpty || !Shapes.joinIsNotEmpty(shape, SHAPE_CHECK_SUPPORT_PLAYER, BooleanOp.AND) + } + private fun isValidPosition(blockPos: BlockPos): Boolean { - if (!Block.canSupportCenter(ply.level, blockPos.below(), Direction.UP) && !Block.canSupportRigidBlock(ply.level, blockPos.below()) && ply.level.getBlockState(blockPos.below()).material != Material.LEAVES) { + if (!canSupportPlayer(blockPos.below()) && !isWall(blockPos.below().below())) { return false } - val a = ply.level.getBlockState(blockPos) - val b = ply.level.getBlockState(blockPos.above()) - - if (a.isAir && b.isAir) { - return true - } - - if (a.getCollisionShape(ply.level, blockPos).isEmpty && b.getCollisionShape(ply.level, blockPos, CollisionContext.of(ply)).isEmpty) { - return true - } - - return false + return !isWall(blockPos.below()) && isAirGap(blockPos) && isAirGap(blockPos.above()) } private data class TraceResult(val pos: BlockPos? = null, val phasedBlocks: Collection = listOf()) { @@ -115,22 +135,36 @@ class EnderTeleporterFeature(capability: MatteryPlayerCapability) : AndroidActiv return TraceResult.EMPTY } + val testPositions = ply.genericPositions() + if ( result.direction == Direction.UP && isValidPosition(result.blockPos.above()) && - shortestDistanceBetween(ply.genericPositions(), result.blockPos.above().asVector()) <= ServerConfig.EnderTeleporter.MAX_DISTANCE + shortestDistanceBetween(testPositions, result.blockPos.above().asVector()) <= ServerConfig.EnderTeleporter.MAX_DISTANCE ) { return TraceResult(result.blockPos.above()) } - for (y in 0 .. ServerConfig.EnderTeleporter.MAX_PHASE_DISTANCE) { - val pos = result.blockPos + BlockPos(0, y + 1, 0) + for (y in (if (ply.level.getBlockState(result.blockPos).`is`(BlockTags.CLIMBABLE)) -1 else 0) .. ServerConfig.EnderTeleporter.MAX_PHASE_DISTANCE) { + val pos = BlockPos(result.blockPos.x, result.blockPos.y + y + 1, result.blockPos.z) - if (isValidPosition(pos) && shortestDistanceBetween(ply.genericPositions(), pos.asVector()) <= ServerConfig.EnderTeleporter.MAX_DISTANCE) { + if (isValidPosition(pos) && shortestDistanceBetween(testPositions, pos.asVector()) <= ServerConfig.EnderTeleporter.MAX_DISTANCE) { return TraceResult(pos, if (y != 0) ImmutableList(y + 1) { result.blockPos + BlockPos(0, it, 0) } else listOf()) } } + // no valid positions... + if (isAirGap(result.blockPos)) { + // try to find ground below... + for (y in 0 downTo -ServerConfig.EnderTeleporter.MAX_PHASE_DISTANCE) { + val pos = BlockPos(result.blockPos.x, result.blockPos.y + y, result.blockPos.z) + + if (isValidPosition(pos) && shortestDistanceBetween(testPositions, pos.asVector()) <= ServerConfig.EnderTeleporter.MAX_DISTANCE) { + return TraceResult(pos, if (y != 0) ImmutableList(-y + 1) { result.blockPos - BlockPos(0, it, 0) } else listOf()) + } + } + } + return TraceResult.EMPTY } @@ -237,6 +271,9 @@ class EnderTeleporterFeature(capability: MatteryPlayerCapability) : AndroidActiv DynamicBufferSource(maximalInitialBufferSize = 256) } + private val SHAPE_CHECK_NOT_FENCE = Block.box(6.0, 17.0, 6.0, 10.0, 31.0, 10.0) + private val SHAPE_CHECK_SUPPORT_PLAYER = Block.box(6.0, 0.0, 6.0, 10.0, 16.0, 10.0) + val SPRITE = ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/item/black_hole.png").element(0f, 0f, 16f, 16f, 16f, 16f) val POWER_COST_DESCRIPTION =