From 77507561395c44cfb0528877375f50fede34b3a8 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 18 Mar 2026 03:14:45 +0000 Subject: [PATCH 1/6] auto portal builder module --- .../module/modules/player/AutoPortal.kt | 321 ++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt diff --git a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt new file mode 100644 index 000000000..ee9f12e90 --- /dev/null +++ b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt @@ -0,0 +1,321 @@ +/* + * Copyright 2026 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.module.modules.player + +import baritone.api.pathing.goals.GoalXZ +import com.lambda.config.AutomationConfig.Companion.setDefaultAutomationConfig +import com.lambda.config.applyEdits +import com.lambda.config.groups.WorldLineSettings +import com.lambda.config.settings.complex.Bind +import com.lambda.config.settings.complex.KeybindSetting.Companion.onRelease +import com.lambda.context.SafeContext +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.mc.renderer.ImmediateRenderer.Companion.immediateRenderer +import com.lambda.graphics.util.DirectionMask.buildSideMesh +import com.lambda.interaction.BaritoneManager +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.managers.hotbar.HotbarRequest +import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest +import com.lambda.interaction.material.StackSelection.Companion.selectStack +import com.lambda.module.Module +import com.lambda.module.modules.player.AutoPortal.PosHandler.obiPositions +import com.lambda.module.tag.ModuleTag +import com.lambda.task.RootTask.run +import com.lambda.task.Task +import com.lambda.task.tasks.BuildTask.Companion.build +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.isEmpty +import com.lambda.util.BlockUtils.isNotEmpty +import com.lambda.util.InputUtils.isSatisfied +import com.lambda.util.NamedEnum +import com.lambda.util.extension.blockColor +import com.lambda.util.math.setAlpha +import com.lambda.util.player.SlotUtils.hotbarAndInventorySlots +import com.lambda.util.player.SlotUtils.hotbarSlots +import net.minecraft.block.Blocks +import net.minecraft.item.FlintAndSteelItem +import net.minecraft.item.Items +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket +import net.minecraft.util.Hand +import net.minecraft.util.hit.BlockHitResult +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import net.minecraft.util.math.Direction + +object AutoPortal : Module( + name = "AutoPortal", + description = "Automatically places and lights a nether portal", + tag = ModuleTag.PLAYER +) { + private enum class Group(override val displayName: String) : NamedEnum { + General("General"), + Render("Render") + } + + private val previewPlace by setting("Preview Place", Bind.EMPTY, "The keybind to preview the portal placement and subsequentially place the portal").group(Group.General) + .onRelease { + buildTask?.cancel() + val posStateMap = + obiPositions.associateWith { + TargetState.Block(Blocks.OBSIDIAN) + } + //ToDo: implement non placement interactions like flint and steel in the build sim, in turn, simulating portal lighting too. +// + portalPositions.associateWith { +// TargetState.Block(Blocks.NETHER_PORTAL) +// } + buildTask = posStateMap + .build() + .thenOrNull { + if (light) LightTask(PosHandler.currAnchorPos.up(), walkIn) + else null + } + .finally { + buildTask = null + } + .run() + } + private val corners by setting("Corners", false).group(Group.General) + private val light by setting("Light", true, "Attempts to automatically light the portal after building").group(Group.General) + private val walkIn by setting("Walk In", true, "Automatically paths into the portal with baritone") { light }.group(Group.General) + private val forwardOffset by setting("Forward Offset", 2, 0..10).group(Group.General) + private val sidewaysOffset by setting("Sideways Offset", 0, -5..5).group(Group.General) + private val yOffset by setting("Y Offset", -1, -5..5).group(Group.General) + private val lockToGround by setting("Lock To Ground", true).group(Group.General) + private val allowUpwardShift by setting("Allow Upward Shift", true).group(Group.General) + + private val depthTest by setting("Depth Test", false).group(Group.Render) + private val interpolate by setting("Interpolate", true, "Interpolates the portal renders from position to position").group(Group.Render) + private val outlineConfig = WorldLineSettings(c = this, baseGroup = arrayOf(Group.Render)).apply { + applyEdits { + hide(::startColor, ::endColor) + } + } + + private var buildTask: Task<*>? = null + + init { + setDefaultAutomationConfig { + applyEdits { + hideGroup(eatConfig) + hotbarConfig::tickStageMask.edit { + defaultValue(mutableSetOf(TickEvent.Pre)) + } + } + } + + listen { + PosHandler.tick() + } + + immediateRenderer("AutoPortal Immediate Renderer", { depthTest }) { safeContext -> + if (!previewPlace.isSatisfied()) return@immediateRenderer + with (safeContext) { + val obiColor = blockColor(Blocks.OBSIDIAN.defaultState, BlockPos.ORIGIN) + obiPositions + .map { Pair(it, Box(it)) } + .forEach { posAndBox -> + box(posAndBox.second, outlineConfig) { + colors(obiColor.setAlpha(0.3), obiColor) + hideSides(buildSideMesh(posAndBox.first) { it in obiPositions }.inv()) + } + } + } + } + } + + private object PosHandler { + var currAnchorPos: BlockPos = BlockPos.ORIGIN + private set + private var prevAnchorPos = currAnchorPos + + var obiPositions = emptyList() + private set + var portalPositions = emptyList() + private set + + private val originObiPositions = getOriginObiPositions() + private val originObiPositionsWithCorners = getOriginObiPositions(true) + private val originPortalPositions = getOriginPortalPositions() + + context(safeContext: SafeContext) + fun tick() = + with(safeContext) { + if (!previewPlace.isSatisfied()) return@with + val offsetDir = when (val playerHorizontal = player.horizontalFacing) { + Direction.UP, + Direction.DOWN -> Direction.EAST + else -> playerHorizontal + } + + val baseAnchorPos = player.blockPos + .offset(offsetDir, forwardOffset) + .offset(offsetDir.rotateYClockwise(), sidewaysOffset) + .offset(Direction.UP, yOffset) + + val lockedAnchorPos = if (lockToGround) run { + var scanPos = baseAnchorPos + if (blockState(scanPos).isNotEmpty && allowUpwardShift) { + while (blockState(scanPos).isEmpty && scanPos.y < 320) { + scanPos = scanPos.up() + } + if (scanPos.y >= 320) return@run null + } else { + while (blockState(scanPos.down()).isEmpty && scanPos.y > -64) { + scanPos = scanPos.down() + } + if (scanPos.y <= -64) return@run null + } + scanPos + } else baseAnchorPos + + if (lockedAnchorPos == currAnchorPos || lockedAnchorPos == null) return@with + + prevAnchorPos = currAnchorPos + currAnchorPos = lockedAnchorPos + val originObi = + if (corners) originObiPositionsWithCorners + else originObiPositions + obiPositions = originObi + .rotatedTo(offsetDir) + .map { it.add(lockedAnchorPos) } + portalPositions = originPortalPositions + .rotatedTo(offsetDir) + .map { it.add(lockedAnchorPos) } + } + + private fun List.rotatedTo(direction: Direction): List = + map { pos -> + when (direction) { + Direction.EAST -> pos + Direction.SOUTH -> BlockPos(pos.z - 1, pos.y, -pos.x) + Direction.WEST -> BlockPos(-pos.x, pos.y, -pos.z) + else -> BlockPos(-pos.z + 1, pos.y, pos.x) + } + } + + private fun getOriginObiPositions(corners: Boolean = false) = + buildList { + (-1..2).forEach { x -> + (0..4).forEach { y -> + if (x > -1 && x < 2 && y > 0 && y < 4) return@forEach + if (!corners && (x == -1 || x == 2) && (y == 0 || y == 4)) return@forEach + add(BlockPos(0, y, x)) + } + } + } + + private fun getOriginPortalPositions() = + buildList { + (0..1).forEach { x -> + (1..3).forEach { y -> + add(BlockPos(0, x, y)) + } + } + } + } + + private class LightTask( + private val pos: BlockPos, + private val walkIn: Boolean + ) : Task() { + override val name = "Lighting portal at $pos" + + init { + listen { + withFlintAndSteel { + swapPacket() + interaction.sendSequencedPacket(world) { sequence -> + PlayerInteractBlockC2SPacket( + Hand.OFF_HAND, + BlockHitResult( + pos.down().toCenterPos(), + Direction.UP, + pos, + false, + false + ), + sequence + ) + } + swapPacket() + if (walkIn) { + BaritoneManager.setGoalAndPath(GoalXZ(pos.x, pos.z)) + } + success() + } + } + } + + private fun SafeContext.swapPacket() = + connection.sendPacket( + PlayerActionC2SPacket( + PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, + BlockPos.ORIGIN, + Direction.DOWN + ) + ) + + private fun SafeContext.withFlintAndSteel(block: SafeContext.() -> Unit) { + if (player.mainHandStack.item == Items.FLINT_AND_STEEL) { + block() + return + } + + val sel = selectStack(1) { isItem() } + + val hotbarStack = sel.filterSlots(player.hotbarSlots).firstOrNull() + if (hotbarStack != null) { + val request = HotbarRequest( + hotbarStack.index, + this@AutoPortal, + keepTicks = 0 + ).submit(queueIfMismatchedStage = false) + if (request.done) block() + return + } + + val invSlot = sel.filterSlots(player.hotbarAndInventorySlots).firstOrNull() + if (invSlot == null) { + failure("No Flint and Steel!") + return + } + val hotbarSlotToSwapWith = + player.hotbarSlots.find { slot -> + slot.stack.isEmpty + }?.index ?: 8 + + inventoryRequest { + swap(invSlot.id, hotbarSlotToSwapWith) + action { + val request = HotbarRequest( + hotbarSlotToSwapWith, + this@AutoPortal, + keepTicks = 0, + nowOrNothing = true + ).submit(queueIfMismatchedStage = false) + if (request.done) { + block() + } + } + swap(invSlot.id, hotbarSlotToSwapWith) + }.submit() + } + } +} \ No newline at end of file From 39d16dda34f1a63e0030161f3e7422fe6b95de2b Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 18 Mar 2026 03:41:06 +0000 Subject: [PATCH 2/6] interpolation and inventory setting --- .../module/modules/player/AutoPortal.kt | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt index ee9f12e90..54ff8a6ed 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt @@ -34,7 +34,9 @@ import com.lambda.interaction.managers.hotbar.HotbarRequest import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.module.Module +import com.lambda.module.modules.player.AutoPortal.PosHandler.currAnchorPos import com.lambda.module.modules.player.AutoPortal.PosHandler.obiPositions +import com.lambda.module.modules.player.AutoPortal.PosHandler.prevAnchorPos import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask.run import com.lambda.task.Task @@ -45,7 +47,10 @@ import com.lambda.util.BlockUtils.isNotEmpty import com.lambda.util.InputUtils.isSatisfied import com.lambda.util.NamedEnum import com.lambda.util.extension.blockColor +import com.lambda.util.extension.tickDelta +import com.lambda.util.math.lerp import com.lambda.util.math.setAlpha +import com.lambda.util.math.vec3d import com.lambda.util.player.SlotUtils.hotbarAndInventorySlots import com.lambda.util.player.SlotUtils.hotbarSlots import net.minecraft.block.Blocks @@ -58,6 +63,7 @@ import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d object AutoPortal : Module( name = "AutoPortal", @@ -83,7 +89,7 @@ object AutoPortal : Module( buildTask = posStateMap .build() .thenOrNull { - if (light) LightTask(PosHandler.currAnchorPos.up(), walkIn) + if (light) LightTask(currAnchorPos.up(), walkIn) else null } .finally { @@ -94,7 +100,8 @@ object AutoPortal : Module( private val corners by setting("Corners", false).group(Group.General) private val light by setting("Light", true, "Attempts to automatically light the portal after building").group(Group.General) private val walkIn by setting("Walk In", true, "Automatically paths into the portal with baritone") { light }.group(Group.General) - private val forwardOffset by setting("Forward Offset", 2, 0..10).group(Group.General) + private val inventory by setting("Inventory", true, "Allows access to the players inventory when retrieving a flint and steel for lighting the portal") + private val forwardOffset by setting("Forward Offset", 3, 0..10).group(Group.General) private val sidewaysOffset by setting("Sideways Offset", 0, -5..5).group(Group.General) private val yOffset by setting("Y Offset", -1, -5..5).group(Group.General) private val lockToGround by setting("Lock To Ground", true).group(Group.General) @@ -115,7 +122,7 @@ object AutoPortal : Module( applyEdits { hideGroup(eatConfig) hotbarConfig::tickStageMask.edit { - defaultValue(mutableSetOf(TickEvent.Pre)) + defaultValue(mutableSetOf(TickEvent.Pre, TickEvent.Input.Post)) } } } @@ -129,7 +136,19 @@ object AutoPortal : Module( with (safeContext) { val obiColor = blockColor(Blocks.OBSIDIAN.defaultState, BlockPos.ORIGIN) obiPositions - .map { Pair(it, Box(it)) } + .map { + val box = Box(it).let { box -> + if (interpolate) { + val offset = lerp( + 1.0 - mc.tickDelta, + Vec3d.ZERO, + prevAnchorPos.subtract(currAnchorPos).vec3d + ) + box.offset(offset) + } else box + } + Pair(it, box) + } .forEach { posAndBox -> box(posAndBox.second, outlineConfig) { colors(obiColor.setAlpha(0.3), obiColor) @@ -143,7 +162,8 @@ object AutoPortal : Module( private object PosHandler { var currAnchorPos: BlockPos = BlockPos.ORIGIN private set - private var prevAnchorPos = currAnchorPos + var prevAnchorPos = currAnchorPos + private set var obiPositions = emptyList() private set @@ -185,7 +205,10 @@ object AutoPortal : Module( scanPos } else baseAnchorPos - if (lockedAnchorPos == currAnchorPos || lockedAnchorPos == null) return@with + if (lockedAnchorPos == currAnchorPos || lockedAnchorPos == null) { + prevAnchorPos = currAnchorPos + return@with + } prevAnchorPos = currAnchorPos currAnchorPos = lockedAnchorPos @@ -291,7 +314,9 @@ object AutoPortal : Module( return } - val invSlot = sel.filterSlots(player.hotbarAndInventorySlots).firstOrNull() + val invSlot = + if (inventory) sel.filterSlots(player.hotbarAndInventorySlots).firstOrNull() + else null if (invSlot == null) { failure("No Flint and Steel!") return From 297562893fc8f21da08fc70fe303f5806b11e7c7 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 18 Mar 2026 04:20:03 +0000 Subject: [PATCH 3/6] renders setting, simulate portal blocks as air to clear space, apply y offset after ground locking, fix ground locking logic --- .../module/modules/player/AutoPortal.kt | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt index 54ff8a6ed..6b6a6dc42 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt @@ -36,6 +36,7 @@ import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.module.Module import com.lambda.module.modules.player.AutoPortal.PosHandler.currAnchorPos import com.lambda.module.modules.player.AutoPortal.PosHandler.obiPositions +import com.lambda.module.modules.player.AutoPortal.PosHandler.portalPositions import com.lambda.module.modules.player.AutoPortal.PosHandler.prevAnchorPos import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask.run @@ -81,6 +82,8 @@ object AutoPortal : Module( val posStateMap = obiPositions.associateWith { TargetState.Block(Blocks.OBSIDIAN) + } + portalPositions.associateWith { + TargetState.Air } //ToDo: implement non placement interactions like flint and steel in the build sim, in turn, simulating portal lighting too. // + portalPositions.associateWith { @@ -100,16 +103,17 @@ object AutoPortal : Module( private val corners by setting("Corners", false).group(Group.General) private val light by setting("Light", true, "Attempts to automatically light the portal after building").group(Group.General) private val walkIn by setting("Walk In", true, "Automatically paths into the portal with baritone") { light }.group(Group.General) - private val inventory by setting("Inventory", true, "Allows access to the players inventory when retrieving a flint and steel for lighting the portal") + private val inventory by setting("Inventory", true, "Allows access to the players inventory when retrieving a flint and steel for lighting the portal").group(Group.General) private val forwardOffset by setting("Forward Offset", 3, 0..10).group(Group.General) private val sidewaysOffset by setting("Sideways Offset", 0, -5..5).group(Group.General) - private val yOffset by setting("Y Offset", -1, -5..5).group(Group.General) + private val yOffset by setting("Y Offset", 0, -5..5).group(Group.General) private val lockToGround by setting("Lock To Ground", true).group(Group.General) private val allowUpwardShift by setting("Allow Upward Shift", true).group(Group.General) - private val depthTest by setting("Depth Test", false).group(Group.Render) - private val interpolate by setting("Interpolate", true, "Interpolates the portal renders from position to position").group(Group.Render) - private val outlineConfig = WorldLineSettings(c = this, baseGroup = arrayOf(Group.Render)).apply { + private val renders by setting("Renders", true).group(Group.Render) + private val depthTest by setting("Depth Test", false) { renders }.group(Group.Render) + private val interpolate by setting("Interpolate", true, "Interpolates the portal renders from position to position") { renders }.group(Group.Render) + private val outlineConfig = WorldLineSettings(c = this, baseGroup = arrayOf(Group.Render)) { renders }.apply { applyEdits { hide(::startColor, ::endColor) } @@ -132,6 +136,7 @@ object AutoPortal : Module( } immediateRenderer("AutoPortal Immediate Renderer", { depthTest }) { safeContext -> + if (!renders) return@immediateRenderer if (!previewPlace.isSatisfied()) return@immediateRenderer with (safeContext) { val obiColor = blockColor(Blocks.OBSIDIAN.defaultState, BlockPos.ORIGIN) @@ -187,40 +192,44 @@ object AutoPortal : Module( val baseAnchorPos = player.blockPos .offset(offsetDir, forwardOffset) .offset(offsetDir.rotateYClockwise(), sidewaysOffset) - .offset(Direction.UP, yOffset) val lockedAnchorPos = if (lockToGround) run { var scanPos = baseAnchorPos - if (blockState(scanPos).isNotEmpty && allowUpwardShift) { - while (blockState(scanPos).isEmpty && scanPos.y < 320) { - scanPos = scanPos.up() + if (blockState(scanPos).isNotEmpty) { + if (allowUpwardShift) { + while (blockState(scanPos).isNotEmpty && scanPos.y < 320) { + scanPos = scanPos.up() + } } - if (scanPos.y >= 320) return@run null - } else { - while (blockState(scanPos.down()).isEmpty && scanPos.y > -64) { - scanPos = scanPos.down() + if (!allowUpwardShift || scanPos.y >= 320) { + scanPos = baseAnchorPos + while (blockState(scanPos.down()).isEmpty && scanPos.y > -64) { + scanPos = scanPos.down() + } + if (scanPos.y <= -64) return@run null } - if (scanPos.y <= -64) return@run null } scanPos } else baseAnchorPos - if (lockedAnchorPos == currAnchorPos || lockedAnchorPos == null) { + val yOffsetAnchorPos = lockedAnchorPos?.offset(Direction.UP, yOffset) + + if (yOffsetAnchorPos == currAnchorPos || yOffsetAnchorPos == null) { prevAnchorPos = currAnchorPos return@with } prevAnchorPos = currAnchorPos - currAnchorPos = lockedAnchorPos + currAnchorPos = yOffsetAnchorPos val originObi = if (corners) originObiPositionsWithCorners else originObiPositions obiPositions = originObi .rotatedTo(offsetDir) - .map { it.add(lockedAnchorPos) } + .map { it.add(yOffsetAnchorPos) } portalPositions = originPortalPositions .rotatedTo(offsetDir) - .map { it.add(lockedAnchorPos) } + .map { it.add(yOffsetAnchorPos) } } private fun List.rotatedTo(direction: Direction): List = @@ -248,7 +257,7 @@ object AutoPortal : Module( buildList { (0..1).forEach { x -> (1..3).forEach { y -> - add(BlockPos(0, x, y)) + add(BlockPos(0, y, x)) } } } From 41a61378c1b440430fe8dede94f56fce1379a438 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 18 Mar 2026 13:38:57 +0000 Subject: [PATCH 4/6] allowUpwardShift description, fillAlpha setting, remove redundant when statement, and use GoalBlock instead of XZ goal --- .../lambda/module/modules/player/AutoPortal.kt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt index 6b6a6dc42..937938c18 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt @@ -17,7 +17,7 @@ package com.lambda.module.modules.player -import baritone.api.pathing.goals.GoalXZ +import baritone.api.pathing.goals.GoalBlock import com.lambda.config.AutomationConfig.Companion.setDefaultAutomationConfig import com.lambda.config.applyEdits import com.lambda.config.groups.WorldLineSettings @@ -108,11 +108,12 @@ object AutoPortal : Module( private val sidewaysOffset by setting("Sideways Offset", 0, -5..5).group(Group.General) private val yOffset by setting("Y Offset", 0, -5..5).group(Group.General) private val lockToGround by setting("Lock To Ground", true).group(Group.General) - private val allowUpwardShift by setting("Allow Upward Shift", true).group(Group.General) + private val allowUpwardShift by setting("Allow Upward Shift", true, "Allows shifting the portal up to find ground when it would be placed inside blocks").group(Group.General) private val renders by setting("Renders", true).group(Group.Render) - private val depthTest by setting("Depth Test", false) { renders }.group(Group.Render) private val interpolate by setting("Interpolate", true, "Interpolates the portal renders from position to position") { renders }.group(Group.Render) + private val fillAlpha by setting("Fill Alpha", 0.3, 0.0..1.0, 0.01) { renders }.group(Group.Render) + private val depthTest by setting("Depth Test", false) { renders }.group(Group.Render) private val outlineConfig = WorldLineSettings(c = this, baseGroup = arrayOf(Group.Render)) { renders }.apply { applyEdits { hide(::startColor, ::endColor) @@ -156,7 +157,7 @@ object AutoPortal : Module( } .forEach { posAndBox -> box(posAndBox.second, outlineConfig) { - colors(obiColor.setAlpha(0.3), obiColor) + colors(obiColor.setAlpha(fillAlpha), obiColor) hideSides(buildSideMesh(posAndBox.first) { it in obiPositions }.inv()) } } @@ -183,11 +184,7 @@ object AutoPortal : Module( fun tick() = with(safeContext) { if (!previewPlace.isSatisfied()) return@with - val offsetDir = when (val playerHorizontal = player.horizontalFacing) { - Direction.UP, - Direction.DOWN -> Direction.EAST - else -> playerHorizontal - } + val offsetDir = player.horizontalFacing val baseAnchorPos = player.blockPos .offset(offsetDir, forwardOffset) @@ -288,7 +285,7 @@ object AutoPortal : Module( } swapPacket() if (walkIn) { - BaritoneManager.setGoalAndPath(GoalXZ(pos.x, pos.z)) + BaritoneManager.setGoalAndPath(GoalBlock(currAnchorPos.up())) } success() } From 07dabd3803dd7ab1c5810984aa66b7f1306138f1 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 18 Mar 2026 14:51:31 +0000 Subject: [PATCH 5/6] use onPress and onRelease to hide and show the preview to account for screen checks --- .../kotlin/com/lambda/module/modules/player/AutoPortal.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt index 937938c18..cbc19c356 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt @@ -22,6 +22,7 @@ import com.lambda.config.AutomationConfig.Companion.setDefaultAutomationConfig import com.lambda.config.applyEdits import com.lambda.config.groups.WorldLineSettings import com.lambda.config.settings.complex.Bind +import com.lambda.config.settings.complex.KeybindSetting.Companion.onPress import com.lambda.config.settings.complex.KeybindSetting.Companion.onRelease import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent @@ -77,7 +78,9 @@ object AutoPortal : Module( } private val previewPlace by setting("Preview Place", Bind.EMPTY, "The keybind to preview the portal placement and subsequentially place the portal").group(Group.General) + .onPress { preview = true } .onRelease { + preview = false buildTask?.cancel() val posStateMap = obiPositions.associateWith { @@ -120,6 +123,7 @@ object AutoPortal : Module( } } + private var preview = false private var buildTask: Task<*>? = null init { @@ -137,8 +141,7 @@ object AutoPortal : Module( } immediateRenderer("AutoPortal Immediate Renderer", { depthTest }) { safeContext -> - if (!renders) return@immediateRenderer - if (!previewPlace.isSatisfied()) return@immediateRenderer + if (!renders || !preview) return@immediateRenderer with (safeContext) { val obiColor = blockColor(Blocks.OBSIDIAN.defaultState, BlockPos.ORIGIN) obiPositions From 30b32a1ffbc263d1e697f8d7d667e053588b2dac Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Wed, 18 Mar 2026 15:20:09 +0000 Subject: [PATCH 6/6] fix lockToGround logic --- .../module/modules/player/AutoPortal.kt | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt index cbc19c356..9bbdb79de 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/AutoPortal.kt @@ -111,7 +111,7 @@ object AutoPortal : Module( private val sidewaysOffset by setting("Sideways Offset", 0, -5..5).group(Group.General) private val yOffset by setting("Y Offset", 0, -5..5).group(Group.General) private val lockToGround by setting("Lock To Ground", true).group(Group.General) - private val allowUpwardShift by setting("Allow Upward Shift", true, "Allows shifting the portal up to find ground when it would be placed inside blocks").group(Group.General) + private val allowUpwardShift by setting("Allow Upward Shift", true, "Allows shifting the portal up to find ground when it would be placed inside blocks") { lockToGround }.group(Group.General) private val renders by setting("Renders", true).group(Group.Render) private val interpolate by setting("Interpolate", true, "Interpolates the portal renders from position to position") { renders }.group(Group.Render) @@ -193,24 +193,9 @@ object AutoPortal : Module( .offset(offsetDir, forwardOffset) .offset(offsetDir.rotateYClockwise(), sidewaysOffset) - val lockedAnchorPos = if (lockToGround) run { - var scanPos = baseAnchorPos - if (blockState(scanPos).isNotEmpty) { - if (allowUpwardShift) { - while (blockState(scanPos).isNotEmpty && scanPos.y < 320) { - scanPos = scanPos.up() - } - } - if (!allowUpwardShift || scanPos.y >= 320) { - scanPos = baseAnchorPos - while (blockState(scanPos.down()).isEmpty && scanPos.y > -64) { - scanPos = scanPos.down() - } - if (scanPos.y <= -64) return@run null - } - } - scanPos - } else baseAnchorPos + val lockedAnchorPos = + if (lockToGround) lockToGround(baseAnchorPos) + else baseAnchorPos val yOffsetAnchorPos = lockedAnchorPos?.offset(Direction.UP, yOffset) @@ -232,6 +217,24 @@ object AutoPortal : Module( .map { it.add(yOffsetAnchorPos) } } + private fun SafeContext.lockToGround(pos: BlockPos): BlockPos? { + var scanPos = pos + val upShifting = blockState(scanPos).isNotEmpty && allowUpwardShift + if (upShifting) { + while (blockState(scanPos).isNotEmpty && scanPos.y < 320) { + scanPos = scanPos.up() + } + } + if (!upShifting || scanPos.y >= 320) { + scanPos = pos + while (blockState(scanPos.down()).isEmpty && scanPos.y > -64) { + scanPos = scanPos.down() + } + if (scanPos.y <= -64) return null + } + return scanPos + } + private fun List.rotatedTo(direction: Direction): List = map { pos -> when (direction) {