1717
1818package com.lambda.module.modules.world
1919
20+ import com.lambda.Lambda.mc
2021import com.lambda.config.AutomationConfig.Companion.setDefaultAutomationConfig
2122import com.lambda.config.applyEdits
2223import com.lambda.config.settings.complex.Bind
@@ -29,6 +30,7 @@ import com.lambda.graphics.mc.renderer.TickedRenderer.Companion.tickedRenderer
2930import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
3031import com.lambda.interaction.construction.simulation.context.BuildContext
3132import com.lambda.interaction.construction.verify.TargetState
33+ import com.lambda.interaction.managers.interacting.InteractRequest
3234import com.lambda.interaction.managers.interacting.InteractRequest.Companion.interactRequest
3335import com.lambda.interaction.managers.rotating.Rotation.Companion.rotation
3436import com.lambda.module.Module
@@ -37,33 +39,61 @@ import com.lambda.threading.runSafeAutomated
3739import com.lambda.util.InputUtils.isSatisfied
3840import com.lambda.util.KeyCode
3941import com.lambda.util.NamedEnum
42+ import com.lambda.util.math.setAlpha
43+ import com.lambda.util.math.vec3d
4044import net.minecraft.block.BlockState
45+ import net.minecraft.fluid.FlowableFluid
4146import net.minecraft.item.BlockItem
47+ import net.minecraft.item.BucketItem
48+ import net.minecraft.item.DebugStickItem
4249import net.minecraft.item.ItemPlacementContext
50+ import net.minecraft.state.property.Properties
4351import net.minecraft.util.Hand
52+ import net.minecraft.util.hit.BlockHitResult
4453import net.minecraft.util.math.BlockPos
45- import net.minecraft.util.math.Box
54+ import net.minecraft.util.math.Vec3d
55+ import net.minecraft.util.math.random.Random
4656import net.minecraft.world.RaycastContext
4757import org.lwjgl.glfw.GLFW
58+ import java.awt.Color
4859import java.util.concurrent.ConcurrentLinkedQueue
4960
5061object AirPlace : Module(
5162 name = " AirPlace" ,
5263 description = " Allows placing blocks in air" ,
5364 tag = ModuleTag .WORLD
5465) {
55- enum class Group (override val displayName : String ) : NamedEnum {
66+ private enum class Group (override val displayName : String ) : NamedEnum {
5667 General (" General" ),
5768 Render (" Render" )
5869 }
5970
6071 private var distance by setting(" Distance" , 4.0 , 1.0 .. 7.0 , 0.01 ).group(Group .General )
61- private val scrollBind by setting(" Scroll Bind" , Bind (KeyCode .Unbound .code, GLFW .GLFW_MOD_CONTROL ), " Allows you to hold the ctrl key and scroll to adjust distance" ).group(Group .General )
72+ private val buckets by setting(" Buckets" , true , " Enables air placement for buckets filled with fluids" ).group(Group .General )
73+ private val distanceScrollBind by setting(" Distance Scroll Bind" , Bind (KeyCode .Unbound .code, GLFW .GLFW_MOD_CONTROL ), " Allows you to hold the given key and scroll to adjust distance" ).group(Group .General )
74+ private val rotationScrollBind by setting(" Rotation Scroll Bind" , Bind (KeyCode .Unbound .code, GLFW .GLFW_MOD_ALT ), " Allows you to hold the given key and scroll to adjust the rotation of the block you're placing" ).group(Group .General )
75+
76+ private val renderState by setting(" Render State" , true ).group(Group .Render )
77+ private val lineColor by setting(" Line Color" , Color .WHITE ).group(Group .Render )
78+ private val stateAlpha by setting(" State Alpha" , 0.5 , 0.01 .. 1.0 , 0.01 ).group(Group .Render )
6279
6380 private var placementPos: BlockPos ? = null
81+ private var backingState: BlockState ? = null
6482 private var placementState: BlockState ? = null
6583 private val pendingInteractions = ConcurrentLinkedQueue <BuildContext >()
6684
85+ private var request: InteractRequest ? = null
86+ private var extraPlaceTicks = 0
87+
88+ private val rotatingProperties = arrayOf(
89+ Properties .ROTATION ,
90+ Properties .FACING ,
91+ Properties .HORIZONTAL_FACING ,
92+ Properties .HORIZONTAL_AXIS ,
93+ Properties .AXIS ,
94+ Properties .HOPPER_FACING
95+ )
96+
6797 init {
6898 setDefaultAutomationConfig {
6999 applyEdits {
@@ -72,56 +102,120 @@ object AirPlace : Module(
72102 }
73103
74104 listen<TickEvent .Pre > {
105+ if (request?.done == false ) {
106+ airPlace()
107+ extraPlaceTicks++
108+ if (extraPlaceTicks > 40 ) request = null
109+ return @listen
110+ } else {
111+ extraPlaceTicks = 0
112+ request = null
113+ }
114+
75115 val selectedStack = player.inventory.selectedStack
76- val blockItem = selectedStack.item as ? BlockItem ? : run {
77- placementPos = null
78- placementState = null
116+ val blockItem = selectedStack.item as ? BlockItem
117+ if (blockItem != null ) {
118+ val placementContext = ItemPlacementContext (
119+ world,
120+ player, Hand .MAIN_HAND ,
121+ selectedStack,
122+ getHitResult()
123+ )
124+ setPosAndState(placementContext.blockPos, blockItem.getPlacementState(placementContext))
79125 return @listen
80126 }
81- val raycastEnd = player.rotation.vector.multiply(distance).add(player.eyePos)
82- val raycastContext = RaycastContext (
83- player.eyePos,
84- raycastEnd,
85- RaycastContext .ShapeType .OUTLINE ,
86- RaycastContext .FluidHandling .NONE ,
87- player
88- )
89- val placementContext = ItemPlacementContext (
90- world,
91- player, Hand .MAIN_HAND ,
92- selectedStack,
93- world.raycast(raycastContext),
94- )
95- placementPos = placementContext.blockPos
96- placementState = blockItem.getPlacementState(placementContext)
127+
128+ if (buckets) run bucket@{
129+ val item = selectedStack.item
130+ if (item !is BucketItem ) return @bucket
131+ val fluid = item.fluid as ? FlowableFluid ? : return @bucket
132+ if (item.fluid !is FlowableFluid ) return @bucket
133+ placementPos = getHitResult().blockPos
134+ placementState = fluid.defaultState.blockState
135+ }
136+ setPosAndState()
137+ return @listen
97138 }
98139
99140 listen<PlayerEvent .Interact .Block > { if (airPlace()) it.cancel() }
100141 listen<PlayerEvent .Interact .Item > { if (airPlace()) it.cancel() }
101142
102143 tickedRenderer(" Air Place Ticked Renderer" ) { safeContext ->
103144 placementPos?.let { pos ->
104- val boxes = placementState?.getOutlineShape(safeContext.world, pos)?.boundingBoxes
105- ? : listOf (Box (0.0 , 0.0 , 0.0 , 1.0 , 1.0 , 1.0 ))
106- boxes.forEach { box ->
107- box(box.offset(pos)) { hideFill() }
145+ placementState?.let { state ->
146+ val boxes = state.getOutlineShape(safeContext.world, pos).boundingBoxes.map { it.offset(pos) }
147+ boxes.forEach { box ->
148+ box(box) {
149+ hideFill()
150+ outlineColor(lineColor)
151+ }
152+ }
153+ if (renderState) {
154+ val model = mc.bakedModelManager.blockModels.getModel(state)
155+ val parts = model.getParts(Random .create())
156+ parts.forEach { part ->
157+ model(
158+ part,
159+ pos = pos.vec3d,
160+ scale = Vec3d (1.0 , 1.0 , 1.0 ),
161+ pixelPerfect = true ,
162+ color = Color .WHITE .setAlpha(stateAlpha)
163+ )
164+ }
165+ }
108166 }
109167 }
110168 }
111169
112170 listen<ButtonEvent .Mouse .Scroll > { event ->
113- if (! scrollBind.isSatisfied()) return @listen
114- event.cancel()
115- distance + = event.delta.y
171+ if (distanceScrollBind.isSatisfied()) {
172+ event.cancel()
173+ distance + = event.delta.y
174+ return @listen
175+ } else if (rotationScrollBind.isSatisfied()) {
176+ event.cancel()
177+ placementState = placementState?.withSteppedRotation(event.delta.y < 0 )
178+ }
116179 }
117180 }
118181
182+ private fun setPosAndState (pos : BlockPos ? = null, state : BlockState ? = null) {
183+ if (state == = backingState) {
184+ placementPos = pos
185+ return
186+ }
187+ backingState = state
188+ placementState = state
189+ }
190+
191+ private fun BlockState.withSteppedRotation (reverse : Boolean ): BlockState {
192+ var rotatedState = this
193+ rotatingProperties.forEach { property ->
194+ if (property in rotatedState) {
195+ rotatedState = DebugStickItem .cycle(rotatedState, property, reverse)
196+ }
197+ }
198+ return rotatedState
199+ }
200+
201+ private fun SafeContext.getHitResult (): BlockHitResult {
202+ val raycastEnd = player.rotation.vector.multiply(distance).add(player.eyePos)
203+ val raycastContext = RaycastContext (
204+ player.eyePos,
205+ raycastEnd,
206+ RaycastContext .ShapeType .OUTLINE ,
207+ RaycastContext .FluidHandling .NONE ,
208+ player
209+ )
210+ return world.raycast(raycastContext)
211+ }
212+
119213 private fun SafeContext.airPlace (): Boolean {
120214 if (player.inventory.selectedStack.item !is BlockItem ) return false
121215 placementPos?.let { pos ->
122216 placementState?.let { state ->
123217 runSafeAutomated {
124- mapOf (pos to TargetState .State (state))
218+ request = mapOf (pos to TargetState .State (state))
125219 .simulate()
126220 .interactRequest(pendingInteractions)
127221 ?.submit()
0 commit comments