diff --git a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAltitudeControl.kt b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAltitudeControl.kt index 5852c0515..209f1420f 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAltitudeControl.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAltitudeControl.kt @@ -19,20 +19,22 @@ package com.lambda.module.modules.movement import com.lambda.config.AutomationConfig.Companion.setDefaultAutomationConfig import com.lambda.config.applyEdits +import com.lambda.context.SafeContext +import com.lambda.event.events.ClientEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotationRequest import com.lambda.module.Module import com.lambda.module.modules.movement.BetterFirework.startFirework import com.lambda.module.tag.ModuleTag -import com.lambda.threading.runSafe import com.lambda.util.Communication.info import com.lambda.util.NamedEnum import com.lambda.util.SpeedUnit -import com.lambda.util.Timer +import com.lambda.util.math.distCenter import com.lambda.util.player.hasFirework import net.minecraft.client.network.ClientPlayerEntity -import net.minecraft.text.Text.literal +import net.minecraft.client.world.ClientWorld +import net.minecraft.util.math.ChunkPos import net.minecraft.util.math.Vec3d import kotlin.time.Duration.Companion.seconds import kotlin.time.TimeSource @@ -79,13 +81,16 @@ object ElytraAltitudeControl : Module( val pitch40SpeedThreshold by setting("Speed Threshold", 41f, 10f..100f, .5f, description = "Speed at which to start pitching up") { usePitch40OnHeight }.group(Group.Pitch40Control) val pitch40UseFireworkOnUpTrajectory by setting("Use Firework On Up Trajectory", false, "Use fireworks when converting speed to altitude in the Pitch 40 maneuver") { usePitch40OnHeight }.group(Group.Pitch40Control) + 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) + 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) + var controlState = ControlState.AttitudeControl var state = Pitch40State.GainSpeed var lastAngle = pitch40UpStartAngle var lastCycleFinish = TimeSource.Monotonic.markNow() var lastY = 0.0 - val usageDelay = Timer() + val usageDelay = com.lambda.util.Timer() init { setDefaultAutomationConfig { @@ -94,90 +99,22 @@ object ElytraAltitudeControl : Module( } } + listen { + val timerValue = getTimerValue() + if (timerValue != null) { + it.speed = timerValue + } + } + listen { - if (!player.isGliding) return@listen - run { + if (player.isGliding) { when (controlState) { - ControlState.AttitudeControl -> { - if (disableOnFirework && player.hasFirework) { - return@run - } - if (usePitch40OnHeight) { - if (player.y < minHeightForPitch40) { - controlState = ControlState.Pitch40Fly - lastY = player.pos.y - return@run - } - } - val outputPitch = when (controlValue) { - Mode.Speed -> { - speedController.getOutput(targetSpeed, player.flySpeed(horizontalSpeed).toDouble()) - } - Mode.Altitude -> { - -1 * altitudeController.getOutput(targetAltitude.toDouble(), player.y) // Negative because in minecraft pitch > 0 is looking down not up - } - }.coerceIn(-maxPitchAngle, maxPitchAngle) - rotationRequest { pitch(outputPitch) }.submit() - - if (usageDelay.timePassed(2.seconds) && !player.hasFirework) { - if (useFireworkOnHeight && minHeight > player.y) { - usageDelay.reset() - runSafe { - startFirework(true) - } - } - if (useFireworkOnSpeed && minSpeed > player.flySpeed()) { - usageDelay.reset() - runSafe { - startFirework(true) - } - } - } - } - ControlState.Pitch40Fly -> when (state) { - Pitch40State.GainSpeed -> { - rotationRequest { pitch(pitch40DownAngle) }.submit() - if (player.flySpeed() > pitch40SpeedThreshold) { - state = Pitch40State.PitchUp - } - } - Pitch40State.PitchUp -> { - lastAngle -= 5f - rotationRequest { pitch(lastAngle) }.submit() - if (lastAngle <= pitch40UpStartAngle) { - state = Pitch40State.FlyUp - if (pitch40UseFireworkOnUpTrajectory) { - runSafe { - startFirework(true) - } - } - } - } - Pitch40State.FlyUp -> { - lastAngle += pitch40AngleChangeRate - rotationRequest { pitch(lastAngle) }.submit() - if (lastAngle >= 0f) { - state = Pitch40State.GainSpeed - if (logHeightGain) { - var timeDelta = lastCycleFinish.elapsedNow().inWholeMilliseconds - var heightDelta = player.pos.y - lastY - var heightPerMinute = (heightDelta) / (timeDelta / 1000.0) * 60.0 - info(literal("Height gained this cycle: %.2f in %.2f seconds (%.2f blocks/min)".format(heightDelta, timeDelta / 1000.0, heightPerMinute))) - } - - lastCycleFinish = TimeSource.Monotonic.markNow() - lastY = player.pos.y - if (pitch40ExitHeight < player.y) { - controlState = ControlState.AttitudeControl - speedController.reset() - altitudeController.reset() - } - } - } - } + ControlState.AttitudeControl -> updateAltitudeControls() + ControlState.Pitch40Fly -> updatePitch40Controls() } + + lastPos = player.pos } - lastPos = player.pos } onEnable { @@ -190,6 +127,125 @@ object ElytraAltitudeControl : Module( } } + private fun SafeContext.getTimerValue(): Double? { + if (!useTimerOnChunkLoad) return null + return nearestUnloadedChunk(world, player) + ?.distCenter(player.pos) + ?.let { + if (it <= timerMinChunkDistance * 16.0) { + val speedFactor = 0.1f + (it / (timerMinChunkDistance * 16.0)) * 0.9f + return@let speedFactor.coerceIn(0.1, 1.0) + } else { + return@let null + } + } + } + + private fun SafeContext.updateAltitudeControls() { + if (disableOnFirework && player.hasFirework) return + + if (usePitch40OnHeight) { + if (player.y < minHeightForPitch40) { + controlState = ControlState.Pitch40Fly + lastY = player.pos.y + return + } + } + + val outputPitch = when (controlValue) { + Mode.Speed -> speedController.getOutput(targetSpeed, player.flySpeed(horizontalSpeed).toDouble()) + Mode.Altitude -> -1 * altitudeController.getOutput(targetAltitude.toDouble(), player.y) // Negative because in minecraft pitch > 0 is looking down not up + }.coerceIn(-maxPitchAngle, maxPitchAngle) + + rotationRequest { + yaw(player.yaw) + pitch(outputPitch) + }.submit() + + if (usageDelay.delayIfPassed(2.seconds) && !player.hasFirework) { + if (useFireworkOnHeight && minHeight > player.y) + startFirework(true) + + if (useFireworkOnSpeed && minSpeed > player.flySpeed()) + startFirework(true) + } + } + + private fun SafeContext.updatePitch40Controls() { + when (state) { + Pitch40State.GainSpeed -> { + rotationRequest { pitch(pitch40DownAngle) }.submit() + + if (player.flySpeed() > pitch40SpeedThreshold) + state = Pitch40State.PitchUp + } + Pitch40State.PitchUp -> { + lastAngle -= 5f + rotationRequest { pitch(lastAngle) }.submit() + if (lastAngle <= pitch40UpStartAngle) { + state = Pitch40State.FlyUp + + if (pitch40UseFireworkOnUpTrajectory) + startFirework(true) + } + } + Pitch40State.FlyUp -> { + lastAngle += pitch40AngleChangeRate + rotationRequest { pitch(lastAngle) }.submit() + if (lastAngle >= 0f) { + state = Pitch40State.GainSpeed + + if (logHeightGain) { + val timeDelta = lastCycleFinish.elapsedNow().inWholeMilliseconds + val heightDelta = player.pos.y - lastY + val heightPerMinute = (heightDelta) / (timeDelta / 1000.0) * 60.0 + + info("Height gained this cycle: %.2f in %.2f seconds (%.2f blocks/min)".format(heightDelta, timeDelta / 1000.0, heightPerMinute)) + } + + lastCycleFinish = TimeSource.Monotonic.markNow() + lastY = player.pos.y + + if (pitch40ExitHeight < player.y) { + controlState = ControlState.AttitudeControl + speedController.reset() + altitudeController.reset() + } + } + } + } + } + + fun nearestUnloadedChunk(world: ClientWorld, player: ClientPlayerEntity): ChunkPos? { + val scanRangeInt = 25 + var nearestChunk: ChunkPos? = null + var nearestDistance = Double.MAX_VALUE + val playerChunk = player.chunkPos + + for (x in -scanRangeInt.. Double, val valueD: () -> Double, val valueI: () -> Double, val constant: () -> Double) { var accumulator = 0.0 // Integral term accumulator var lastDiff = 0.0 @@ -234,7 +290,8 @@ object ElytraAltitudeControl : Module( SpeedControl("Speed Control"), AltitudeControl("Altitude Control"), Pitch40Control("Pitch 40 Control"), - Rotation("Rotation") + Rotation("Rotation"), + TimerControls("Timer Controls"), } enum class Pitch40State { @@ -242,4 +299,4 @@ object ElytraAltitudeControl : Module( PitchUp, FlyUp, } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/lambda/module/modules/movement/Timer.kt b/src/main/kotlin/com/lambda/module/modules/movement/Timer.kt index 3e89bd6ee..19dedd5de 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/Timer.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/Timer.kt @@ -23,15 +23,16 @@ import com.lambda.module.Module import com.lambda.module.tag.ModuleTag object Timer : Module( - name = "Timer", - description = "Modify client tick speed.", - tag = ModuleTag.MOVEMENT, + name = "Timer", + description = "Modify client tick speed.", + tag = ModuleTag.MOVEMENT, ) { - private val timer by setting("Timer", 1.0, 0.0..10.0, 0.01) + @JvmStatic + var timer by setting("Timer", 1.0, 0.0..10.0, 0.01) - init { - listen { - it.speed = timer.coerceAtLeast(0.05) - } - } + init { + listen { + it.speed = timer.coerceAtLeast(0.05) + } + } }