From 808e30d14f7097a45958735186b2c0f14ff70606 Mon Sep 17 00:00:00 2001 From: Ic3Tank <61137113+IceTank@users.noreply.github.com> Date: Sun, 7 Dec 2025 15:39:16 +0100 Subject: [PATCH 1/9] Add ElytraAttitudeControl module --- .../module/modules/movement/BetterFirework.kt | 1 + .../modules/movement/ElytraAttitudeControl.kt | 149 ++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt diff --git a/src/main/kotlin/com/lambda/module/modules/movement/BetterFirework.kt b/src/main/kotlin/com/lambda/module/modules/movement/BetterFirework.kt index 820541f40..3a0116937 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/BetterFirework.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/BetterFirework.kt @@ -205,6 +205,7 @@ object BetterFirework : Module( * Use a firework from the hotbar or inventory if possible. * Return true if a firework has been used */ + @JvmStatic fun SafeContext.startFirework(silent: Boolean) { val stack = selectStack(count = 1) { isItem(Items.FIREWORK_ROCKET) } diff --git a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt new file mode 100644 index 000000000..b22cac332 --- /dev/null +++ b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2025 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.movement + +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen +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.NamedEnum +import com.lambda.util.SpeedUnit +import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.entity.EntityType +import net.minecraft.util.math.Vec3d + +object ElytraAttitudeControl : Module( + name = "ElytraAttitudeControl", + description = "Automatically control attitude or speed while elytra flying", + tag = ModuleTag.MOVEMENT, +) { + val controlValue by setting("Control Value", Mode.Altitude) + + val maxPitchAngle by setting("Max Pitch Angle", 45.0, 0.0..90.0, 1.0, unit = "°", description = "Maximum pitch angle") + val disableOnFirework by setting("Disable On Firework", false, description = "Disables the module when a firework is used") + + val targetAltitude by setting("Target Altitude", 120, 0..256, 10, unit = " blocks", description = "Adjusts pitch to control altitude") { controlValue == Mode.Altitude } + val altitudeControllerP by setting("Altitude Control P", 1.2, 0.0..2.0, 0.05).group(Group.AltitudeControl) + val altitudeControllerD by setting("Altitude Control D", 0.85, 0.0..1.0, 0.05).group(Group.AltitudeControl) + val altitudeControllerI by setting("Altitude Control I", 0.04, 0.0..1.0, 0.05).group(Group.AltitudeControl) + val altitudeControllerConst by setting("Altitude Control Const", 0.0, 0.0..10.0, 0.1).group(Group.AltitudeControl) + + val targetSpeed by setting("Target Speed", 28.0, 0.1..50.0, 0.1, unit = " m/s", description = "Adjusts pitch to control speed") { controlValue == Mode.Speed } + val horizontalSpeed by setting("Horizontal Speed", false, description = "Uses horizontal speed instead of total speed for speed control") { controlValue == Mode.Speed } + val speedControllerP by setting("Speed Control P", 6.75, 0.0..10.0, 0.05).group(Group.SpeedControl) + val speedControllerD by setting("Speed Control D", 4.5, 0.0..5.0, 0.05).group(Group.SpeedControl) + val speedControllerI by setting("Speed Control I", 0.3, 0.0..1.0, 0.05).group(Group.SpeedControl) + + val useFirework by setting("Use Firework", false, "Automatically use fireworks to maintain speed or height") + val minHeight by setting("Min Height", 50, 0..256, 10, unit = " blocks", description = "Minimum height to use fireworks") { useFirework } + val minSpeed by setting("Min Speed", 15.0, 0.1..50.0, 0.1, unit = " m/s", description = "Minimum speed to use fireworks") { useFirework } + + var lastPos : Vec3d = Vec3d.ZERO + val speedController: PIController = PIController({ speedControllerP }, { speedControllerD }, { speedControllerI }, { 0.0 }) + val altitudeController: PIController = PIController({ altitudeControllerP }, { altitudeControllerD }, { altitudeControllerI }, { altitudeControllerConst }) + + var lastUsedFirework = 0L + + init { + listen { + if (!player.isGliding) return@listen + if (player.hasFirework && disableOnFirework) return@listen + + val outputPitch = when (controlValue) { + Mode.Speed -> { + var speed = player.pos.subtract(lastPos) + if (horizontalSpeed) { + speed = Vec3d(speed.x, 0.0, speed.z) + } + + speedController.getOutput(targetSpeed, SpeedUnit.MetersPerSecond.convertFromMinecraft(speed.length())) + } + Mode.Altitude -> { + val currentAltitude = player.y + -1 * altitudeController.getOutput(targetAltitude.toDouble(), currentAltitude) // Negative because in minecraft pitch > 0 is looking down not up + } + } + val newPitch = outputPitch.coerceIn(-maxPitchAngle, maxPitchAngle) +// lookAt(Rotation(player.yaw, newPitch.toFloat())).requestBy(this@ElytraAutopilot) // TODO: Use this when rotation system accepts pitch changes + player.pitch = newPitch.toFloat() + + lastPos = player.pos + + if (useFirework) { + val currentTime = System.currentTimeMillis() + if (lastUsedFirework + 2000 > currentTime) { + return@listen + } + + if (player.hasFirework) { + return@listen + } + + if (minHeight >= player.y || SpeedUnit.MetersPerSecond.convertFromMinecraft(player.velocity.length()) < minSpeed) { + lastUsedFirework = currentTime + runSafe { + startFirework(true) + } + } + } + } + + onEnable { + speedController.reset() + altitudeController.reset() + lastPos = player.pos + } + } + + val ClientPlayerEntity.hasFirework: Boolean + get() = clientWorld.getEntitiesByType( + EntityType.FIREWORK_ROCKET, + boundingBox.expand(4.0), + { it.distanceTo(this) < 4.0 } + ).isNotEmpty() + + class PIController(val valueP: () -> Double, val valueD: () -> Double, val valueI: () -> Double, val constant: () -> Double) { + var accumulator = 0.0 // Integral term accumulator + var lastDiff = 0.0 + fun getOutput(target: Double, current: Double): Double { + val diff = target - current + val diffDt = diff - lastDiff + accumulator += diff + + accumulator = accumulator.coerceIn(-100.0, 100.0) // Prevent integral windup + lastDiff = diff + + return diffDt * valueD() + diff * valueP() + accumulator * valueI() + constant() + } + + fun reset() { + accumulator = 0.0 + } + } + + enum class Mode { + Speed, + Altitude; + } + enum class Group(override val displayName: String) : NamedEnum { + SpeedControl("Speed Control"), + AltitudeControl("Altitude Control"); + } +} \ No newline at end of file From 9a681850cdbdebb306dd4a7f9dc1195f76567e93 Mon Sep 17 00:00:00 2001 From: Ic3Tank <61137113+IceTank@users.noreply.github.com> Date: Sun, 7 Dec 2025 18:51:11 +0100 Subject: [PATCH 2/9] ElytraAttitudeControl: Replace System.currentTimeMillis() Change default settings --- .../modules/movement/ElytraAttitudeControl.kt | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt index b22cac332..c2cdf8884 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt @@ -25,9 +25,11 @@ import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe import com.lambda.util.NamedEnum import com.lambda.util.SpeedUnit +import com.lambda.util.Timer import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.entity.EntityType import net.minecraft.util.math.Vec3d +import kotlin.time.Duration.Companion.seconds object ElytraAttitudeControl : Module( name = "ElytraAttitudeControl", @@ -51,15 +53,17 @@ object ElytraAttitudeControl : Module( val speedControllerD by setting("Speed Control D", 4.5, 0.0..5.0, 0.05).group(Group.SpeedControl) val speedControllerI by setting("Speed Control I", 0.3, 0.0..1.0, 0.05).group(Group.SpeedControl) - val useFirework by setting("Use Firework", false, "Automatically use fireworks to maintain speed or height") - val minHeight by setting("Min Height", 50, 0..256, 10, unit = " blocks", description = "Minimum height to use fireworks") { useFirework } - val minSpeed by setting("Min Speed", 15.0, 0.1..50.0, 0.1, unit = " m/s", description = "Minimum speed to use fireworks") { useFirework } + val useFireworkOnHeight by setting("Use Firework On Height", false, "Use fireworks when below a certain height") + val minHeight by setting("Min Height", 50, 0..256, 10, unit = " blocks", description = "Minimum height to use firework") { useFireworkOnHeight } + + val useFireworkOnSpeed by setting("Use Firework On Speed", false, "Use fireworks based on speed") + val minSpeed by setting("Min Speed", 20.0, 0.1..50.0, 0.1, unit = " m/s", description = "Minimum speed to use fireworks") { useFireworkOnSpeed } var lastPos : Vec3d = Vec3d.ZERO val speedController: PIController = PIController({ speedControllerP }, { speedControllerD }, { speedControllerI }, { 0.0 }) val altitudeController: PIController = PIController({ altitudeControllerP }, { altitudeControllerD }, { altitudeControllerI }, { altitudeControllerConst }) - var lastUsedFirework = 0L + val usageDelay = Timer() init { listen { @@ -86,18 +90,15 @@ object ElytraAttitudeControl : Module( lastPos = player.pos - if (useFirework) { - val currentTime = System.currentTimeMillis() - if (lastUsedFirework + 2000 > currentTime) { - return@listen - } - - if (player.hasFirework) { - return@listen + if (usageDelay.timePassed(2.seconds) && !player.hasFirework) { + if (useFireworkOnHeight && minHeight > player.y) { + usageDelay.reset() + runSafe { + startFirework(true) + } } - - if (minHeight >= player.y || SpeedUnit.MetersPerSecond.convertFromMinecraft(player.velocity.length()) < minSpeed) { - lastUsedFirework = currentTime + if (useFireworkOnSpeed && minSpeed > SpeedUnit.MetersPerSecond.convertFromMinecraft(player.velocity.length())) { + usageDelay.reset() runSafe { startFirework(true) } From e3c4bb47d860ed8c4da6837e9357e882776dda29 Mon Sep 17 00:00:00 2001 From: Ic3Tank <61137113+IceTank@users.noreply.github.com> Date: Sun, 7 Dec 2025 18:54:50 +0100 Subject: [PATCH 3/9] ElytraAttitudeControl: Lint --- .../modules/movement/ElytraAttitudeControl.kt | 184 +++++++++--------- 1 file changed, 96 insertions(+), 88 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt index c2cdf8884..12738dd67 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt @@ -32,119 +32,127 @@ import net.minecraft.util.math.Vec3d import kotlin.time.Duration.Companion.seconds object ElytraAttitudeControl : Module( - name = "ElytraAttitudeControl", - description = "Automatically control attitude or speed while elytra flying", - tag = ModuleTag.MOVEMENT, + name = "ElytraAttitudeControl", + description = "Automatically control attitude or speed while elytra flying", + tag = ModuleTag.MOVEMENT, ) { - val controlValue by setting("Control Value", Mode.Altitude) + val controlValue by setting("Control Value", Mode.Altitude) - val maxPitchAngle by setting("Max Pitch Angle", 45.0, 0.0..90.0, 1.0, unit = "°", description = "Maximum pitch angle") - val disableOnFirework by setting("Disable On Firework", false, description = "Disables the module when a firework is used") + val maxPitchAngle by setting("Max Pitch Angle", 45.0, 0.0..90.0, 1.0, unit = "°", description = "Maximum pitch angle") + val disableOnFirework by setting("Disable On Firework", false, description = "Disables the module when a firework is used") - val targetAltitude by setting("Target Altitude", 120, 0..256, 10, unit = " blocks", description = "Adjusts pitch to control altitude") { controlValue == Mode.Altitude } + val targetAltitude by setting("Target Altitude", 120, 0..256, 10, unit = " blocks", description = "Adjusts pitch to control altitude") + { controlValue == Mode.Altitude } val altitudeControllerP by setting("Altitude Control P", 1.2, 0.0..2.0, 0.05).group(Group.AltitudeControl) val altitudeControllerD by setting("Altitude Control D", 0.85, 0.0..1.0, 0.05).group(Group.AltitudeControl) val altitudeControllerI by setting("Altitude Control I", 0.04, 0.0..1.0, 0.05).group(Group.AltitudeControl) val altitudeControllerConst by setting("Altitude Control Const", 0.0, 0.0..10.0, 0.1).group(Group.AltitudeControl) - val targetSpeed by setting("Target Speed", 28.0, 0.1..50.0, 0.1, unit = " m/s", description = "Adjusts pitch to control speed") { controlValue == Mode.Speed } - val horizontalSpeed by setting("Horizontal Speed", false, description = "Uses horizontal speed instead of total speed for speed control") { controlValue == Mode.Speed } + val targetSpeed by setting("Target Speed", 28.0, 0.1..50.0, 0.1, unit = " m/s", description = "Adjusts pitch to control speed") + { controlValue == Mode.Speed } + val horizontalSpeed by setting("Horizontal Speed", false, description = "Uses horizontal speed instead of total speed for speed control") + { controlValue == Mode.Speed } val speedControllerP by setting("Speed Control P", 6.75, 0.0..10.0, 0.05).group(Group.SpeedControl) val speedControllerD by setting("Speed Control D", 4.5, 0.0..5.0, 0.05).group(Group.SpeedControl) val speedControllerI by setting("Speed Control I", 0.3, 0.0..1.0, 0.05).group(Group.SpeedControl) val useFireworkOnHeight by setting("Use Firework On Height", false, "Use fireworks when below a certain height") - val minHeight by setting("Min Height", 50, 0..256, 10, unit = " blocks", description = "Minimum height to use firework") { useFireworkOnHeight } + val minHeight by setting("Min Height", 50, 0..256, 10, unit = " blocks", description = "Minimum height to use firework") + { useFireworkOnHeight } val useFireworkOnSpeed by setting("Use Firework On Speed", false, "Use fireworks based on speed") - val minSpeed by setting("Min Speed", 20.0, 0.1..50.0, 0.1, unit = " m/s", description = "Minimum speed to use fireworks") { useFireworkOnSpeed } + val minSpeed by setting("Min Speed", 20.0, 0.1..50.0, 0.1, unit = " m/s", description = "Minimum speed to use fireworks") + { useFireworkOnSpeed } - var lastPos : Vec3d = Vec3d.ZERO - val speedController: PIController = PIController({ speedControllerP }, { speedControllerD }, { speedControllerI }, { 0.0 }) - val altitudeController: PIController = PIController({ altitudeControllerP }, { altitudeControllerD }, { altitudeControllerI }, { altitudeControllerConst }) + var lastPos: Vec3d = Vec3d.ZERO + val speedController: PIController = PIController({ speedControllerP }, { speedControllerD }, { speedControllerI }, { 0.0 }) + val altitudeController: PIController = PIController( + { altitudeControllerP }, { altitudeControllerD }, { altitudeControllerI }, + { altitudeControllerConst }) val usageDelay = Timer() - init { - listen { - if (!player.isGliding) return@listen - if (player.hasFirework && disableOnFirework) return@listen + init { + listen { + if (!player.isGliding) return@listen + if (player.hasFirework && disableOnFirework) return@listen - val outputPitch = when (controlValue) { - Mode.Speed -> { - var speed = player.pos.subtract(lastPos) - if (horizontalSpeed) { + val outputPitch = when (controlValue) { + Mode.Speed -> { + var speed = player.pos.subtract(lastPos) + if (horizontalSpeed) { speed = Vec3d(speed.x, 0.0, speed.z) - } - - speedController.getOutput(targetSpeed, SpeedUnit.MetersPerSecond.convertFromMinecraft(speed.length())) - } - Mode.Altitude -> { - val currentAltitude = player.y - -1 * altitudeController.getOutput(targetAltitude.toDouble(), currentAltitude) // Negative because in minecraft pitch > 0 is looking down not up - } - } - val newPitch = outputPitch.coerceIn(-maxPitchAngle, maxPitchAngle) -// lookAt(Rotation(player.yaw, newPitch.toFloat())).requestBy(this@ElytraAutopilot) // TODO: Use this when rotation system accepts pitch changes - player.pitch = newPitch.toFloat() - - lastPos = player.pos - - if (usageDelay.timePassed(2.seconds) && !player.hasFirework) { - if (useFireworkOnHeight && minHeight > player.y) { - usageDelay.reset() - runSafe { - startFirework(true) - } - } - if (useFireworkOnSpeed && minSpeed > SpeedUnit.MetersPerSecond.convertFromMinecraft(player.velocity.length())) { + } + + speedController.getOutput(targetSpeed, SpeedUnit.MetersPerSecond.convertFromMinecraft(speed.length())) + } + Mode.Altitude -> { + val currentAltitude = player.y + -1 * altitudeController.getOutput(targetAltitude.toDouble(), currentAltitude) // Negative because in minecraft pitch > 0 is looking down not up + } + } + val newPitch = outputPitch.coerceIn(-maxPitchAngle, maxPitchAngle) + // lookAt(Rotation(player.yaw, newPitch.toFloat())).requestBy(this@ElytraAutopilot) // TODO: Use this when rotation system accepts pitch changes + player.pitch = newPitch.toFloat() + + lastPos = player.pos + + if (usageDelay.timePassed(2.seconds) && !player.hasFirework) { + if (useFireworkOnHeight && minHeight > player.y) { usageDelay.reset() - runSafe { - startFirework(true) - } - } - } - } - - onEnable { - speedController.reset() - altitudeController.reset() - lastPos = player.pos - } - } - - val ClientPlayerEntity.hasFirework: Boolean - get() = clientWorld.getEntitiesByType( - EntityType.FIREWORK_ROCKET, - boundingBox.expand(4.0), - { it.distanceTo(this) < 4.0 } - ).isNotEmpty() + runSafe { + startFirework(true) + } + } + if (useFireworkOnSpeed && minSpeed > SpeedUnit.MetersPerSecond.convertFromMinecraft(player.velocity.length())) { + usageDelay.reset() + runSafe { + startFirework(true) + } + } + } + } + + onEnable { + speedController.reset() + altitudeController.reset() + lastPos = player.pos + } + } + + val ClientPlayerEntity.hasFirework: Boolean + get() = clientWorld.getEntitiesByType( + EntityType.FIREWORK_ROCKET, + boundingBox.expand(4.0), + { it.distanceTo(this) < 4.0 } + ).isNotEmpty() class PIController(val valueP: () -> Double, val valueD: () -> Double, val valueI: () -> Double, val constant: () -> Double) { - var accumulator = 0.0 // Integral term accumulator + var accumulator = 0.0 // Integral term accumulator var lastDiff = 0.0 - fun getOutput(target: Double, current: Double): Double { - val diff = target - current - val diffDt = diff - lastDiff + fun getOutput(target: Double, current: Double): Double { + val diff = target - current + val diffDt = diff - lastDiff accumulator += diff - accumulator = accumulator.coerceIn(-100.0, 100.0) // Prevent integral windup - lastDiff = diff - - return diffDt * valueD() + diff * valueP() + accumulator * valueI() + constant() - } - - fun reset() { - accumulator = 0.0 - } - } - - enum class Mode { - Speed, - Altitude; - } - enum class Group(override val displayName: String) : NamedEnum { - SpeedControl("Speed Control"), - AltitudeControl("Altitude Control"); - } + accumulator = accumulator.coerceIn(-100.0, 100.0) // Prevent integral windup + lastDiff = diff + + return diffDt * valueD() + diff * valueP() + accumulator * valueI() + constant() + } + + fun reset() { + accumulator = 0.0 + } + } + + enum class Mode { + Speed, + Altitude; + } + + enum class Group(override val displayName: String) : NamedEnum { + SpeedControl("Speed Control"), + AltitudeControl("Altitude Control"); + } } \ No newline at end of file From 4c8b4f13093e28929fb81bb75d716987cba7d926 Mon Sep 17 00:00:00 2001 From: Ic3Tank <61137113+IceTank@users.noreply.github.com> Date: Sun, 7 Dec 2025 19:34:57 +0100 Subject: [PATCH 4/9] Pitch40: Add Pitch40 --- .../lambda/module/modules/movement/Pitch40.kt | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/main/kotlin/com/lambda/module/modules/movement/Pitch40.kt diff --git a/src/main/kotlin/com/lambda/module/modules/movement/Pitch40.kt b/src/main/kotlin/com/lambda/module/modules/movement/Pitch40.kt new file mode 100644 index 000000000..fe84454b1 --- /dev/null +++ b/src/main/kotlin/com/lambda/module/modules/movement/Pitch40.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2025 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.movement + +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.Communication.info +import com.lambda.util.SpeedUnit +import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.text.Text.literal +import net.minecraft.util.math.Vec3d + + +/* + * @author IceTank + * @since 07.12.2025 +*/ +object Pitch40 : Module( + name = "Pitch40", + description = "Allows you to fly forever", + tag = ModuleTag.MOVEMENT +) { + val logHeightGain by setting("Log Height Gain", true, "Logs the height gained each cycle to the chat") + + val PitchUpDefault = -49f // Start angle when going back up. negative pitch = looking up + val PitchDownDefault = 33f // Best angle for getting speed + val PitchAngleChangeSpeed = 0.5f + val PitchUpSpeedThreshold = 45f + + var state = Pitch40State.GainSpeed + var lastAngle = PitchUpDefault + + var lastPos = Vec3d.ZERO + var lastY = 0.0 + + init { + listen { + when (state) { + Pitch40State.GainSpeed -> { + player.pitch = PitchDownDefault + if (player.flySpeed() > PitchUpSpeedThreshold) { + state = Pitch40State.PitchUp + } + } + Pitch40State.PitchUp -> { + lastAngle -= 5f + player.pitch = lastAngle + if (lastAngle <= PitchUpDefault) { + state = Pitch40State.FlyUp + } + } + Pitch40State.FlyUp -> { + lastAngle += PitchAngleChangeSpeed + player.pitch = lastAngle + if (lastAngle >= 0f) { + state = Pitch40State.GainSpeed + if (logHeightGain) + info(literal("Height gained this cycle: %.2f meters".format(player.pos.y - lastY))) + + lastY = player.pos.y + } + } + } + lastPos = player.pos + } + + onEnable { + state = Pitch40State.GainSpeed + lastPos = player.pos + lastAngle = PitchUpDefault + } + } + + /** + * Get the player's current speed in meters per second. + */ + fun ClientPlayerEntity.flySpeed(): Float { + val delta = this.pos.subtract(lastPos) + return SpeedUnit.MetersPerSecond.convertFromMinecraft(delta.length()).toFloat() + } + + enum class Pitch40State { + GainSpeed, + PitchUp, + FlyUp, + } +} \ No newline at end of file From 87aa4ac4a8f4f5711a94d6fe7c39aca85441d958 Mon Sep 17 00:00:00 2001 From: Ic3Tank <61137113+IceTank@users.noreply.github.com> Date: Mon, 8 Dec 2025 16:18:49 +0100 Subject: [PATCH 5/9] ElytraAttitudeControl: Improve firework detection method --- .../modules/movement/ElytraAttitudeControl.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt index 12738dd67..36485e800 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt @@ -23,11 +23,14 @@ 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.world.fastEntitySearch import net.minecraft.client.network.ClientPlayerEntity -import net.minecraft.entity.EntityType +import net.minecraft.entity.projectile.FireworkRocketEntity +import net.minecraft.text.Text import net.minecraft.util.math.Vec3d import kotlin.time.Duration.Companion.seconds @@ -75,7 +78,7 @@ object ElytraAttitudeControl : Module( init { listen { if (!player.isGliding) return@listen - if (player.hasFirework && disableOnFirework) return@listen + if (disableOnFirework && player.hasFirework) return@listen val outputPitch = when (controlValue) { Mode.Speed -> { @@ -121,11 +124,7 @@ object ElytraAttitudeControl : Module( } val ClientPlayerEntity.hasFirework: Boolean - get() = clientWorld.getEntitiesByType( - EntityType.FIREWORK_ROCKET, - boundingBox.expand(4.0), - { it.distanceTo(this) < 4.0 } - ).isNotEmpty() + get() = runSafe { return fastEntitySearch(4.0) { it.shooter == this.player }.any() } ?: false class PIController(val valueP: () -> Double, val valueD: () -> Double, val valueI: () -> Double, val constant: () -> Double) { var accumulator = 0.0 // Integral term accumulator From b72703a870c1919c7e0c34821d3e06ad100e89c5 Mon Sep 17 00:00:00 2001 From: Ic3Tank <61137113+IceTank@users.noreply.github.com> Date: Mon, 8 Dec 2025 16:20:27 +0100 Subject: [PATCH 6/9] ElytraAttitudeControl: Optimize imports --- .../com/lambda/module/modules/movement/ElytraAttitudeControl.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt index 36485e800..52b984c46 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt @@ -23,14 +23,12 @@ 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.world.fastEntitySearch import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.entity.projectile.FireworkRocketEntity -import net.minecraft.text.Text import net.minecraft.util.math.Vec3d import kotlin.time.Duration.Companion.seconds From 9a8f58a44fab1d0f6d359edfb4f948fd921bf46a Mon Sep 17 00:00:00 2001 From: Ic3Tank <61137113+IceTank@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:32:07 +0100 Subject: [PATCH 7/9] ElytraAttitudeControl: Add pitch40 as method to gain height --- .../modules/movement/ElytraAttitudeControl.kt | 166 ++++++++++++++---- 1 file changed, 134 insertions(+), 32 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt index 52b984c46..b2c5d2cc6 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt @@ -23,14 +23,17 @@ 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.world.fastEntitySearch import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.entity.projectile.FireworkRocketEntity +import net.minecraft.text.Text.literal import net.minecraft.util.math.Vec3d import kotlin.time.Duration.Companion.seconds +import kotlin.time.TimeSource object ElytraAttitudeControl : Module( name = "ElytraAttitudeControl", @@ -49,7 +52,7 @@ object ElytraAttitudeControl : Module( val altitudeControllerI by setting("Altitude Control I", 0.04, 0.0..1.0, 0.05).group(Group.AltitudeControl) val altitudeControllerConst by setting("Altitude Control Const", 0.0, 0.0..10.0, 0.1).group(Group.AltitudeControl) - val targetSpeed by setting("Target Speed", 28.0, 0.1..50.0, 0.1, unit = " m/s", description = "Adjusts pitch to control speed") + val targetSpeed by setting("Target Speed", 20.0, 0.1..50.0, 0.1, unit = " m/s", description = "Adjusts pitch to control speed") { controlValue == Mode.Speed } val horizontalSpeed by setting("Horizontal Speed", false, description = "Uses horizontal speed instead of total speed for speed control") { controlValue == Mode.Speed } @@ -71,53 +74,129 @@ object ElytraAttitudeControl : Module( { altitudeControllerP }, { altitudeControllerD }, { altitudeControllerI }, { altitudeControllerConst }) + val usePitch40OnHeight by setting("Use Pitch 40 On Height", false, "Use Pitch 40 to gain height and speed") + val logHeightGain by setting("Log Height Gain", false, "Logs the height gained each cycle to the chat") + { usePitch40OnHeight }.group(Group.Pitch40Control) + val minHeightForPitch40 by setting("Min Height For Pitch 40", 120, 0..256, 10, unit = " blocks", description = "Minimum height to use Pitch 40") + { usePitch40OnHeight }.group(Group.Pitch40Control) + val pitch40ExitHeight by setting("Exit height", 190, 0..256, 10, unit = " blocks", description = "Height to exit Pitch 40 mode") + { usePitch40OnHeight }.group(Group.Pitch40Control) + val pitch40UpStartAngle by setting("Up Start Angle", -49f, -90f..0f, .5f, description = "Start angle when going back up. negative pitch = looking up") + { usePitch40OnHeight }.group(Group.Pitch40Control) + val pitch40DownAngle by setting("Down Angle", 33f, 0f..90f, .5f, description = "Angle to dive down at to gain speed") + { usePitch40OnHeight }.group(Group.Pitch40Control) + val pitch40AngleChangeRate by setting("Angle Change Rate", 0.5f, 0.1f..5f, 0.01f, description = "Rate at which to increase pitch while in the fly up curve") + { usePitch40OnHeight }.group(Group.Pitch40Control) + 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) + + var controlState = ControlState.AttitudeControl + var state = Pitch40State.GainSpeed + var lastAngle = pitch40UpStartAngle + var lastCycleFinish = TimeSource.Monotonic.markNow() + var lastY = 0.0 + val usageDelay = Timer() init { listen { if (!player.isGliding) return@listen - if (disableOnFirework && player.hasFirework) return@listen + run { + 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) + // lookAt(Rotation(player.yaw, newPitch.toFloat())).requestBy(this@ElytraAutopilot) // TODO: Use this when rotation system accepts pitch changes + player.pitch = outputPitch.toFloat() - val outputPitch = when (controlValue) { - Mode.Speed -> { - var speed = player.pos.subtract(lastPos) - if (horizontalSpeed) { - speed = Vec3d(speed.x, 0.0, speed.z) + 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 -> { + player.pitch = pitch40DownAngle + if (player.flySpeed() > pitch40SpeedThreshold) { + state = Pitch40State.PitchUp + } + } + Pitch40State.PitchUp -> { + lastAngle -= 5f + player.pitch = lastAngle + if (lastAngle <= pitch40UpStartAngle) { + state = Pitch40State.FlyUp + if (pitch40UseFireworkOnUpTrajectory) { + runSafe { + startFirework(true) + } + } + } + } + Pitch40State.FlyUp -> { + lastAngle += pitch40AngleChangeRate + player.pitch = lastAngle + 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))) + } - speedController.getOutput(targetSpeed, SpeedUnit.MetersPerSecond.convertFromMinecraft(speed.length())) - } - Mode.Altitude -> { - val currentAltitude = player.y - -1 * altitudeController.getOutput(targetAltitude.toDouble(), currentAltitude) // Negative because in minecraft pitch > 0 is looking down not up - } - } - val newPitch = outputPitch.coerceIn(-maxPitchAngle, maxPitchAngle) - // lookAt(Rotation(player.yaw, newPitch.toFloat())).requestBy(this@ElytraAutopilot) // TODO: Use this when rotation system accepts pitch changes - player.pitch = newPitch.toFloat() - - lastPos = player.pos - - if (usageDelay.timePassed(2.seconds) && !player.hasFirework) { - if (useFireworkOnHeight && minHeight > player.y) { - usageDelay.reset() - runSafe { - startFirework(true) - } - } - if (useFireworkOnSpeed && minSpeed > SpeedUnit.MetersPerSecond.convertFromMinecraft(player.velocity.length())) { - usageDelay.reset() - runSafe { - startFirework(true) + lastCycleFinish = TimeSource.Monotonic.markNow() + lastY = player.pos.y + if (pitch40ExitHeight < player.y) { + controlState = ControlState.AttitudeControl + speedController.reset() + altitudeController.reset() + } + } + } + } } } } + lastPos = player.pos } onEnable { speedController.reset() altitudeController.reset() lastPos = player.pos + state = Pitch40State.GainSpeed + controlState = ControlState.AttitudeControl + lastAngle = pitch40UpStartAngle } } @@ -143,13 +222,36 @@ object ElytraAttitudeControl : Module( } } + /** + * Get the player's current speed in meters per second. + */ + fun ClientPlayerEntity.flySpeed(onlyHorizontal: Boolean = false): Float { + var delta = this.pos.subtract(lastPos) + if (onlyHorizontal) { + delta = Vec3d(delta.x, 0.0, delta.z) + } + return SpeedUnit.MetersPerSecond.convertFromMinecraft(delta.length()).toFloat() + } + enum class Mode { Speed, Altitude; } + enum class ControlState { + AttitudeControl, + Pitch40Fly + } + enum class Group(override val displayName: String) : NamedEnum { SpeedControl("Speed Control"), - AltitudeControl("Altitude Control"); + AltitudeControl("Altitude Control"), + Pitch40Control("Pitch 40 Control"), + } + + enum class Pitch40State { + GainSpeed, + PitchUp, + FlyUp, } } \ No newline at end of file From b79bca269543bcb42c45ce235679a5b2098a39e6 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:51:40 +0000 Subject: [PATCH 8/9] fixed --- .../modules/movement/ElytraAttitudeControl.kt | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt index b2c5d2cc6..e1ce65781 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt @@ -45,52 +45,37 @@ object ElytraAttitudeControl : Module( val maxPitchAngle by setting("Max Pitch Angle", 45.0, 0.0..90.0, 1.0, unit = "°", description = "Maximum pitch angle") val disableOnFirework by setting("Disable On Firework", false, description = "Disables the module when a firework is used") - val targetAltitude by setting("Target Altitude", 120, 0..256, 10, unit = " blocks", description = "Adjusts pitch to control altitude") - { controlValue == Mode.Altitude } + val targetAltitude by setting("Target Altitude", 120, 0..256, 10, unit = " blocks", description = "Adjusts pitch to control altitude") { controlValue == Mode.Altitude } val altitudeControllerP by setting("Altitude Control P", 1.2, 0.0..2.0, 0.05).group(Group.AltitudeControl) val altitudeControllerD by setting("Altitude Control D", 0.85, 0.0..1.0, 0.05).group(Group.AltitudeControl) val altitudeControllerI by setting("Altitude Control I", 0.04, 0.0..1.0, 0.05).group(Group.AltitudeControl) val altitudeControllerConst by setting("Altitude Control Const", 0.0, 0.0..10.0, 0.1).group(Group.AltitudeControl) - val targetSpeed by setting("Target Speed", 20.0, 0.1..50.0, 0.1, unit = " m/s", description = "Adjusts pitch to control speed") - { controlValue == Mode.Speed } - val horizontalSpeed by setting("Horizontal Speed", false, description = "Uses horizontal speed instead of total speed for speed control") - { controlValue == Mode.Speed } + val targetSpeed by setting("Target Speed", 20.0, 0.1..50.0, 0.1, unit = " m/s", description = "Adjusts pitch to control speed") { controlValue == Mode.Speed } + val horizontalSpeed by setting("Horizontal Speed", false, description = "Uses horizontal speed instead of total speed for speed control") { controlValue == Mode.Speed } val speedControllerP by setting("Speed Control P", 6.75, 0.0..10.0, 0.05).group(Group.SpeedControl) val speedControllerD by setting("Speed Control D", 4.5, 0.0..5.0, 0.05).group(Group.SpeedControl) val speedControllerI by setting("Speed Control I", 0.3, 0.0..1.0, 0.05).group(Group.SpeedControl) val useFireworkOnHeight by setting("Use Firework On Height", false, "Use fireworks when below a certain height") - val minHeight by setting("Min Height", 50, 0..256, 10, unit = " blocks", description = "Minimum height to use firework") - { useFireworkOnHeight } + val minHeight by setting("Min Height", 50, 0..256, 10, unit = " blocks", description = "Minimum height to use firework") { useFireworkOnHeight } val useFireworkOnSpeed by setting("Use Firework On Speed", false, "Use fireworks based on speed") - val minSpeed by setting("Min Speed", 20.0, 0.1..50.0, 0.1, unit = " m/s", description = "Minimum speed to use fireworks") - { useFireworkOnSpeed } + val minSpeed by setting("Min Speed", 20.0, 0.1..50.0, 0.1, unit = " m/s", description = "Minimum speed to use fireworks") { useFireworkOnSpeed } var lastPos: Vec3d = Vec3d.ZERO val speedController: PIController = PIController({ speedControllerP }, { speedControllerD }, { speedControllerI }, { 0.0 }) - val altitudeController: PIController = PIController( - { altitudeControllerP }, { altitudeControllerD }, { altitudeControllerI }, - { altitudeControllerConst }) + val altitudeController: PIController = PIController({ altitudeControllerP }, { altitudeControllerD }, { altitudeControllerI }, { altitudeControllerConst }) val usePitch40OnHeight by setting("Use Pitch 40 On Height", false, "Use Pitch 40 to gain height and speed") - val logHeightGain by setting("Log Height Gain", false, "Logs the height gained each cycle to the chat") - { usePitch40OnHeight }.group(Group.Pitch40Control) - val minHeightForPitch40 by setting("Min Height For Pitch 40", 120, 0..256, 10, unit = " blocks", description = "Minimum height to use Pitch 40") - { usePitch40OnHeight }.group(Group.Pitch40Control) - val pitch40ExitHeight by setting("Exit height", 190, 0..256, 10, unit = " blocks", description = "Height to exit Pitch 40 mode") - { usePitch40OnHeight }.group(Group.Pitch40Control) - val pitch40UpStartAngle by setting("Up Start Angle", -49f, -90f..0f, .5f, description = "Start angle when going back up. negative pitch = looking up") - { usePitch40OnHeight }.group(Group.Pitch40Control) - val pitch40DownAngle by setting("Down Angle", 33f, 0f..90f, .5f, description = "Angle to dive down at to gain speed") - { usePitch40OnHeight }.group(Group.Pitch40Control) - val pitch40AngleChangeRate by setting("Angle Change Rate", 0.5f, 0.1f..5f, 0.01f, description = "Rate at which to increase pitch while in the fly up curve") - { usePitch40OnHeight }.group(Group.Pitch40Control) - 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 logHeightGain by setting("Log Height Gain", false, "Logs the height gained each cycle to the chat") { usePitch40OnHeight }.group(Group.Pitch40Control) + val minHeightForPitch40 by setting("Min Height For Pitch 40", 120, 0..256, 10, unit = " blocks", description = "Minimum height to use Pitch 40") { usePitch40OnHeight }.group(Group.Pitch40Control) + val pitch40ExitHeight by setting("Exit height", 190, 0..256, 10, unit = " blocks", description = "Height to exit Pitch 40 mode") { usePitch40OnHeight }.group(Group.Pitch40Control) + val pitch40UpStartAngle by setting("Up Start Angle", -49f, -90f..0f, .5f, description = "Start angle when going back up. negative pitch = looking up") { usePitch40OnHeight }.group(Group.Pitch40Control) + val pitch40DownAngle by setting("Down Angle", 33f, 0f..90f, .5f, description = "Angle to dive down at to gain speed") { usePitch40OnHeight }.group(Group.Pitch40Control) + val pitch40AngleChangeRate by setting("Angle Change Rate", 0.5f, 0.1f..5f, 0.01f, description = "Rate at which to increase pitch while in the fly up curve") { usePitch40OnHeight }.group(Group.Pitch40Control) + 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) var controlState = ControlState.AttitudeControl var state = Pitch40State.GainSpeed From 051d20b969f1f8acd50f109b8b4293b0bc9e97c5 Mon Sep 17 00:00:00 2001 From: beanbag44 <107891830+beanbag44@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:58:01 +0000 Subject: [PATCH 9/9] use rotation manager --- .../modules/movement/ElytraAttitudeControl.kt | 75 ++++++++++--------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt index e1ce65781..9bda31bbe 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt @@ -17,8 +17,11 @@ package com.lambda.module.modules.movement +import com.lambda.config.groups.RotationSettings import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.managers.rotating.Rotation +import com.lambda.interaction.managers.rotating.visibilty.lookAt import com.lambda.module.Module import com.lambda.module.modules.movement.BetterFirework.startFirework import com.lambda.module.tag.ModuleTag @@ -77,6 +80,8 @@ object ElytraAttitudeControl : 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) + override val rotationConfig = RotationSettings(this, Group.Rotation) + var controlState = ControlState.AttitudeControl var state = Pitch40State.GainSpeed var lastAngle = pitch40UpStartAngle @@ -109,8 +114,7 @@ object ElytraAttitudeControl : Module( -1 * altitudeController.getOutput(targetAltitude.toDouble(), player.y) // Negative because in minecraft pitch > 0 is looking down not up } }.coerceIn(-maxPitchAngle, maxPitchAngle) - // lookAt(Rotation(player.yaw, newPitch.toFloat())).requestBy(this@ElytraAutopilot) // TODO: Use this when rotation system accepts pitch changes - player.pitch = outputPitch.toFloat() + lookAt(Rotation(player.yaw, outputPitch.toFloat())).requestBy(this@ElytraAttitudeControl) if (usageDelay.timePassed(2.seconds) && !player.hasFirework) { if (useFireworkOnHeight && minHeight > player.y) { @@ -127,45 +131,43 @@ object ElytraAttitudeControl : Module( } } } - ControlState.Pitch40Fly -> { - when (state) { - Pitch40State.GainSpeed -> { - player.pitch = pitch40DownAngle - if (player.flySpeed() > pitch40SpeedThreshold) { - state = Pitch40State.PitchUp - } + ControlState.Pitch40Fly -> when (state) { + Pitch40State.GainSpeed -> { + player.pitch = pitch40DownAngle + if (player.flySpeed() > pitch40SpeedThreshold) { + state = Pitch40State.PitchUp } - Pitch40State.PitchUp -> { - lastAngle -= 5f - player.pitch = lastAngle - if (lastAngle <= pitch40UpStartAngle) { - state = Pitch40State.FlyUp - if (pitch40UseFireworkOnUpTrajectory) { - runSafe { - startFirework(true) - } + } + Pitch40State.PitchUp -> { + lastAngle -= 5f + player.pitch = lastAngle + if (lastAngle <= pitch40UpStartAngle) { + state = Pitch40State.FlyUp + if (pitch40UseFireworkOnUpTrajectory) { + runSafe { + startFirework(true) } } } - Pitch40State.FlyUp -> { - lastAngle += pitch40AngleChangeRate - player.pitch = lastAngle - 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))) - } + } + Pitch40State.FlyUp -> { + lastAngle += pitch40AngleChangeRate + player.pitch = lastAngle + 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() - } + lastCycleFinish = TimeSource.Monotonic.markNow() + lastY = player.pos.y + if (pitch40ExitHeight < player.y) { + controlState = ControlState.AttitudeControl + speedController.reset() + altitudeController.reset() } } } @@ -232,6 +234,7 @@ object ElytraAttitudeControl : Module( SpeedControl("Speed Control"), AltitudeControl("Altitude Control"), Pitch40Control("Pitch 40 Control"), + Rotation("Rotation") } enum class Pitch40State {