11/*
2- * Copyright 2025 Lambda
2+ * Copyright 2026 Lambda
33 *
44 * This program is free software: you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -27,21 +27,19 @@ import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotat
2727import com.lambda.module.Module
2828import com.lambda.module.modules.movement.BetterFirework.startFirework
2929import com.lambda.module.tag.ModuleTag
30- import com.lambda.threading.runSafe
3130import com.lambda.util.Communication.info
3231import com.lambda.util.NamedEnum
3332import com.lambda.util.SpeedUnit
33+ import com.lambda.util.math.dist
3434import com.lambda.util.world.fastEntitySearch
3535import net.minecraft.client.network.ClientPlayerEntity
3636import net.minecraft.client.world.ClientWorld
3737import net.minecraft.entity.projectile.FireworkRocketEntity
38- import net.minecraft.text.Text.literal
3938import net.minecraft.util.math.ChunkPos
4039import net.minecraft.util.math.Vec3d
4140import kotlin.time.Duration.Companion.seconds
4241import kotlin.time.TimeSource
4342
44-
4543object ElytraAltitudeControl : Module(
4644 name = " ElytraAttitudeControl" ,
4745 description = " Automatically control attitude or speed while elytra flying" ,
@@ -86,7 +84,7 @@ object ElytraAltitudeControl : Module(
8684
8785 val useTimerOnChunkLoad by setting(" Use Timer On Slow Chunk Loading" , false , " Slows down the game when chunks load slow to keep momentum" ).group(Group .TimerControls )
8886 val timerMinChunkDistance by setting(" Min Chunk Distance" , 4 , 1 .. 20 , 1 , " Min unloaded chunk distance to start timer effect" , unit = " chunks" ) { useTimerOnChunkLoad }.group(Group .TimerControls )
89- val timerReturnValue by setting(" Timer Return Value" , 1.0f , 0.0f .. 1.0f , 0.05f , description = " Timer speed to return when above min chunk distance" ) { useTimerOnChunkLoad }.group(Group .TimerControls )
87+ val timerReturnValue by setting(" Timer Return Value" , 1.0 , 0.0 .. 1.0 , 0.05 , description = " Timer speed to return when above min chunk distance" ) { useTimerOnChunkLoad }.group(Group .TimerControls )
9088
9189 override val rotationConfig = RotationSettings (this , Group .Rotation )
9290
@@ -98,6 +96,9 @@ object ElytraAltitudeControl : Module(
9896
9997 val usageDelay = com.lambda.util.Timer ()
10098
99+ val SafeContext .hasFirework: Boolean
100+ get() = fastEntitySearch<FireworkRocketEntity >(4.0 ) { it.shooter == this .player }.any()
101+
101102 init {
102103 setDefaultAutomationConfig {
103104 applyEdits {
@@ -111,6 +112,7 @@ object ElytraAltitudeControl : Module(
111112 ControlState .AttitudeControl -> updateAltitudeControls()
112113 ControlState .Pitch40Fly -> updatePitch40Controls()
113114 }
115+
114116 updateTimerUsage()
115117 lastPos = player.pos
116118 }
@@ -126,83 +128,76 @@ object ElytraAltitudeControl : Module(
126128 }
127129
128130 onDisable {
129- if (useTimerOnChunkLoad) {
130- Timer .timer = timerReturnValue.toDouble()
131- }
131+ if (useTimerOnChunkLoad)
132+ Timer .timer = timerReturnValue
132133 }
133134 }
134135
135136 private fun SafeContext.updateAltitudeControls () {
136- if (disableOnFirework && hasFirework) {
137- return
138- }
137+ if (disableOnFirework && hasFirework) return
138+
139139 if (usePitch40OnHeight) {
140140 if (player.y < minHeightForPitch40) {
141141 controlState = ControlState .Pitch40Fly
142142 lastY = player.pos.y
143143 return
144144 }
145145 }
146+
146147 val outputPitch = when (controlValue) {
147- Mode .Speed -> {
148- speedController.getOutput(targetSpeed, player.flySpeed(horizontalSpeed).toDouble())
149- }
150- Mode .Altitude -> {
151- - 1 * altitudeController.getOutput(targetAltitude.toDouble(), player.y) // Negative because in minecraft pitch > 0 is looking down not up
152- }
148+ Mode .Speed -> speedController.getOutput(targetSpeed, player.flySpeed(horizontalSpeed).toDouble())
149+ Mode .Altitude -> - 1 * altitudeController.getOutput(targetAltitude.toDouble(), player.y) // Negative because in minecraft pitch > 0 is looking down not up
153150 }.coerceIn(- maxPitchAngle, maxPitchAngle)
154- RotationRequest (Rotation (player.yaw, outputPitch.toFloat()), this @ElytraAltitudeControl).submit()
155151
156- if (usageDelay.timePassed(2 .seconds) && ! hasFirework) {
157- if (useFireworkOnHeight && minHeight > player.y) {
158- usageDelay.reset()
159- runSafe {
160- startFirework(true )
161- }
162- }
163- if (useFireworkOnSpeed && minSpeed > player.flySpeed()) {
164- usageDelay.reset()
165- runSafe {
166- startFirework(true )
167- }
168- }
152+ rotationRequest {
153+ yaw(player.yaw)
154+ pitch(outputPitch)
155+ }.submit()
156+
157+ if (usageDelay.delayIfPassed(2 .seconds) && ! hasFirework) {
158+ if (useFireworkOnHeight && minHeight > player.y)
159+ startFirework(true )
160+
161+ if (useFireworkOnSpeed && minSpeed > player.flySpeed())
162+ startFirework(true )
169163 }
170164 }
171165
172166 private fun SafeContext.updatePitch40Controls () {
173167 when (state) {
174168 Pitch40State .GainSpeed -> {
175169 rotationRequest { pitch(pitch40DownAngle) }.submit()
176- if (player.flySpeed() > pitch40SpeedThreshold) {
170+
171+ if (player.flySpeed() > pitch40SpeedThreshold)
177172 state = Pitch40State .PitchUp
178- }
179173 }
180174 Pitch40State .PitchUp -> {
181175 lastAngle - = 5f
182176 rotationRequest { pitch(lastAngle) }.submit()
183177 if (lastAngle <= pitch40UpStartAngle) {
184178 state = Pitch40State .FlyUp
185- if (pitch40UseFireworkOnUpTrajectory) {
186- runSafe {
187- startFirework(true )
188- }
189- }
179+
180+ if (pitch40UseFireworkOnUpTrajectory)
181+ startFirework(true )
190182 }
191183 }
192184 Pitch40State .FlyUp -> {
193185 lastAngle + = pitch40AngleChangeRate
194186 rotationRequest { pitch(lastAngle) }.submit()
195187 if (lastAngle >= 0f ) {
196188 state = Pitch40State .GainSpeed
189+
197190 if (logHeightGain) {
198191 val timeDelta = lastCycleFinish.elapsedNow().inWholeMilliseconds
199192 val heightDelta = player.pos.y - lastY
200193 val heightPerMinute = (heightDelta) / (timeDelta / 1000.0 ) * 60.0
201- info(literal(" Height gained this cycle: %.2f in %.2f seconds (%.2f blocks/min)" .format(heightDelta, timeDelta / 1000.0 , heightPerMinute)))
194+
195+ info(" Height gained this cycle: %.2f in %.2f seconds (%.2f blocks/min)" .format(heightDelta, timeDelta / 1000.0 , heightPerMinute))
202196 }
203197
204198 lastCycleFinish = TimeSource .Monotonic .markNow()
205199 lastY = player.pos.y
200+
206201 if (pitch40ExitHeight < player.y) {
207202 controlState = ControlState .AttitudeControl
208203 speedController.reset()
@@ -215,27 +210,22 @@ object ElytraAltitudeControl : Module(
215210
216211 private fun SafeContext.updateTimerUsage () {
217212 if (useTimerOnChunkLoad) {
218- val nearestChunkDistance = getNearestUnloadedChunkDistance()
219- if (nearestChunkDistance != - 1 && nearestChunkDistance / 16.0 <= timerMinChunkDistance) {
220- val speedFactor = 0.1f + (nearestChunkDistance.toFloat() / timerMinChunkDistance.toFloat() * 16.0 ) * 0.9f
221- Timer .enable()
222- Timer .timer = speedFactor.coerceIn(0.1 , 1.0 )
223- } else {
224- if (Timer .isEnabled) {
225- Timer .timer = timerReturnValue.toDouble()
213+ nearestUnloadedChunk(world, player)
214+ ?.dist(player.pos)
215+ ?.let {
216+ if (it / 16.0 <= timerMinChunkDistance) {
217+ val speedFactor = 0.1f + (it / timerMinChunkDistance * 16.0 ) * 0.9f
218+ Timer .enable()
219+ Timer .timer = speedFactor.coerceIn(0.1 , 1.0 )
220+ }
221+ }
222+ ? : run {
223+ if (Timer .isEnabled)
224+ Timer .timer = timerReturnValue
226225 }
227- }
228226 }
229227 }
230228
231- val hasFirework: Boolean
232- get() = runSafe { return fastEntitySearch<FireworkRocketEntity >(4.0 ) { it.shooter == player }.any() } ? : false
233-
234- private fun SafeContext.getNearestUnloadedChunkDistance (): Int {
235- val nearestChunk: ChunkPos ? = nearestUnloadedChunk(world, player)
236- return if (nearestChunk != null ) distanceToChunk(nearestChunk, player).toInt() else - 1
237- }
238-
239229 fun nearestUnloadedChunk (world : ClientWorld , player : ClientPlayerEntity ): ChunkPos ? {
240230 val scanRangeInt = 25
241231 var nearestChunk: ChunkPos ? = null
@@ -245,21 +235,23 @@ object ElytraAltitudeControl : Module(
245235 for (x in - scanRangeInt.. < scanRangeInt) {
246236 for (z in - scanRangeInt.. < scanRangeInt) {
247237 val chunkPos = ChunkPos (playerChunk.x + x, playerChunk.z + z)
248- if (world.chunkManager.isChunkLoaded(chunkPos.x, chunkPos.z)) {
238+
239+ if (world.chunkManager.isChunkLoaded(chunkPos.x, chunkPos.z))
249240 continue
250- }
241+
251242 val distance = distanceToChunk(chunkPos, player).toDouble()
252243 if (distance < nearestDistance) {
253244 nearestDistance = distance
254245 nearestChunk = chunkPos
255246 }
256247 }
257248 }
249+
258250 return nearestChunk
259251 }
260252
261253 fun distanceToChunk (chunkPos : ChunkPos , player : ClientPlayerEntity ): Float {
262- val playerPos = player.getPos()
254+ val playerPos = player.pos
263255 val chunkCenter = Vec3d ((chunkPos.startX + 8 ).toDouble(), playerPos.y, (chunkPos.startZ + 8 ).toDouble())
264256 return playerPos.distanceTo(chunkCenter).toFloat()
265257 }
@@ -317,4 +309,4 @@ object ElytraAltitudeControl : Module(
317309 PitchUp ,
318310 FlyUp ,
319311 }
320- }
312+ }
0 commit comments