Skip to content

Commit 9a8f58a

Browse files
committed
ElytraAttitudeControl: Add pitch40 as method to gain height
1 parent 474725b commit 9a8f58a

File tree

1 file changed

+134
-32
lines changed

1 file changed

+134
-32
lines changed

src/main/kotlin/com/lambda/module/modules/movement/ElytraAttitudeControl.kt

Lines changed: 134 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,17 @@ import com.lambda.module.Module
2323
import com.lambda.module.modules.movement.BetterFirework.startFirework
2424
import com.lambda.module.tag.ModuleTag
2525
import com.lambda.threading.runSafe
26+
import com.lambda.util.Communication.info
2627
import com.lambda.util.NamedEnum
2728
import com.lambda.util.SpeedUnit
2829
import com.lambda.util.Timer
2930
import com.lambda.util.world.fastEntitySearch
3031
import net.minecraft.client.network.ClientPlayerEntity
3132
import net.minecraft.entity.projectile.FireworkRocketEntity
33+
import net.minecraft.text.Text.literal
3234
import net.minecraft.util.math.Vec3d
3335
import kotlin.time.Duration.Companion.seconds
36+
import kotlin.time.TimeSource
3437

3538
object ElytraAttitudeControl : Module(
3639
name = "ElytraAttitudeControl",
@@ -49,7 +52,7 @@ object ElytraAttitudeControl : Module(
4952
val altitudeControllerI by setting("Altitude Control I", 0.04, 0.0..1.0, 0.05).group(Group.AltitudeControl)
5053
val altitudeControllerConst by setting("Altitude Control Const", 0.0, 0.0..10.0, 0.1).group(Group.AltitudeControl)
5154

52-
val targetSpeed by setting("Target Speed", 28.0, 0.1..50.0, 0.1, unit = " m/s", description = "Adjusts pitch to control speed")
55+
val targetSpeed by setting("Target Speed", 20.0, 0.1..50.0, 0.1, unit = " m/s", description = "Adjusts pitch to control speed")
5356
{ controlValue == Mode.Speed }
5457
val horizontalSpeed by setting("Horizontal Speed", false, description = "Uses horizontal speed instead of total speed for speed control")
5558
{ controlValue == Mode.Speed }
@@ -71,53 +74,129 @@ object ElytraAttitudeControl : Module(
7174
{ altitudeControllerP }, { altitudeControllerD }, { altitudeControllerI },
7275
{ altitudeControllerConst })
7376

77+
val usePitch40OnHeight by setting("Use Pitch 40 On Height", false, "Use Pitch 40 to gain height and speed")
78+
val logHeightGain by setting("Log Height Gain", false, "Logs the height gained each cycle to the chat")
79+
{ usePitch40OnHeight }.group(Group.Pitch40Control)
80+
val minHeightForPitch40 by setting("Min Height For Pitch 40", 120, 0..256, 10, unit = " blocks", description = "Minimum height to use Pitch 40")
81+
{ usePitch40OnHeight }.group(Group.Pitch40Control)
82+
val pitch40ExitHeight by setting("Exit height", 190, 0..256, 10, unit = " blocks", description = "Height to exit Pitch 40 mode")
83+
{ usePitch40OnHeight }.group(Group.Pitch40Control)
84+
val pitch40UpStartAngle by setting("Up Start Angle", -49f, -90f..0f, .5f, description = "Start angle when going back up. negative pitch = looking up")
85+
{ usePitch40OnHeight }.group(Group.Pitch40Control)
86+
val pitch40DownAngle by setting("Down Angle", 33f, 0f..90f, .5f, description = "Angle to dive down at to gain speed")
87+
{ usePitch40OnHeight }.group(Group.Pitch40Control)
88+
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")
89+
{ usePitch40OnHeight }.group(Group.Pitch40Control)
90+
val pitch40SpeedThreshold by setting("Speed Threshold", 41f, 10f..100f, .5f, description = "Speed at which to start pitching up")
91+
{ usePitch40OnHeight }.group(Group.Pitch40Control)
92+
val pitch40UseFireworkOnUpTrajectory by setting("Use Firework On Up Trajectory", false, "Use fireworks when converting speed to altitude in the Pitch 40 maneuver")
93+
{ usePitch40OnHeight }.group(Group.Pitch40Control)
94+
95+
var controlState = ControlState.AttitudeControl
96+
var state = Pitch40State.GainSpeed
97+
var lastAngle = pitch40UpStartAngle
98+
var lastCycleFinish = TimeSource.Monotonic.markNow()
99+
var lastY = 0.0
100+
74101
val usageDelay = Timer()
75102

76103
init {
77104
listen<TickEvent.Pre> {
78105
if (!player.isGliding) return@listen
79-
if (disableOnFirework && player.hasFirework) return@listen
106+
run {
107+
when (controlState) {
108+
ControlState.AttitudeControl -> {
109+
if (disableOnFirework && player.hasFirework) {
110+
return@run
111+
}
112+
if (usePitch40OnHeight) {
113+
if (player.y < minHeightForPitch40) {
114+
controlState = ControlState.Pitch40Fly
115+
lastY = player.pos.y
116+
return@run
117+
}
118+
}
119+
val outputPitch = when (controlValue) {
120+
Mode.Speed -> {
121+
speedController.getOutput(targetSpeed, player.flySpeed(horizontalSpeed).toDouble())
122+
}
123+
Mode.Altitude -> {
124+
-1 * altitudeController.getOutput(targetAltitude.toDouble(), player.y) // Negative because in minecraft pitch > 0 is looking down not up
125+
}
126+
}.coerceIn(-maxPitchAngle, maxPitchAngle)
127+
// lookAt(Rotation(player.yaw, newPitch.toFloat())).requestBy(this@ElytraAutopilot) // TODO: Use this when rotation system accepts pitch changes
128+
player.pitch = outputPitch.toFloat()
80129

81-
val outputPitch = when (controlValue) {
82-
Mode.Speed -> {
83-
var speed = player.pos.subtract(lastPos)
84-
if (horizontalSpeed) {
85-
speed = Vec3d(speed.x, 0.0, speed.z)
130+
if (usageDelay.timePassed(2.seconds) && !player.hasFirework) {
131+
if (useFireworkOnHeight && minHeight > player.y) {
132+
usageDelay.reset()
133+
runSafe {
134+
startFirework(true)
135+
}
136+
}
137+
if (useFireworkOnSpeed && minSpeed > player.flySpeed()) {
138+
usageDelay.reset()
139+
runSafe {
140+
startFirework(true)
141+
}
142+
}
143+
}
86144
}
145+
ControlState.Pitch40Fly -> {
146+
when (state) {
147+
Pitch40State.GainSpeed -> {
148+
player.pitch = pitch40DownAngle
149+
if (player.flySpeed() > pitch40SpeedThreshold) {
150+
state = Pitch40State.PitchUp
151+
}
152+
}
153+
Pitch40State.PitchUp -> {
154+
lastAngle -= 5f
155+
player.pitch = lastAngle
156+
if (lastAngle <= pitch40UpStartAngle) {
157+
state = Pitch40State.FlyUp
158+
if (pitch40UseFireworkOnUpTrajectory) {
159+
runSafe {
160+
startFirework(true)
161+
}
162+
}
163+
}
164+
}
165+
Pitch40State.FlyUp -> {
166+
lastAngle += pitch40AngleChangeRate
167+
player.pitch = lastAngle
168+
if (lastAngle >= 0f) {
169+
state = Pitch40State.GainSpeed
170+
if (logHeightGain) {
171+
var timeDelta = lastCycleFinish.elapsedNow().inWholeMilliseconds
172+
var heightDelta = player.pos.y - lastY
173+
var heightPerMinute = (heightDelta) / (timeDelta / 1000.0) * 60.0
174+
info(literal("Height gained this cycle: %.2f in %.2f seconds (%.2f blocks/min)".format(heightDelta, timeDelta / 1000.0, heightPerMinute)))
175+
}
87176

88-
speedController.getOutput(targetSpeed, SpeedUnit.MetersPerSecond.convertFromMinecraft(speed.length()))
89-
}
90-
Mode.Altitude -> {
91-
val currentAltitude = player.y
92-
-1 * altitudeController.getOutput(targetAltitude.toDouble(), currentAltitude) // Negative because in minecraft pitch > 0 is looking down not up
93-
}
94-
}
95-
val newPitch = outputPitch.coerceIn(-maxPitchAngle, maxPitchAngle)
96-
// lookAt(Rotation(player.yaw, newPitch.toFloat())).requestBy(this@ElytraAutopilot) // TODO: Use this when rotation system accepts pitch changes
97-
player.pitch = newPitch.toFloat()
98-
99-
lastPos = player.pos
100-
101-
if (usageDelay.timePassed(2.seconds) && !player.hasFirework) {
102-
if (useFireworkOnHeight && minHeight > player.y) {
103-
usageDelay.reset()
104-
runSafe {
105-
startFirework(true)
106-
}
107-
}
108-
if (useFireworkOnSpeed && minSpeed > SpeedUnit.MetersPerSecond.convertFromMinecraft(player.velocity.length())) {
109-
usageDelay.reset()
110-
runSafe {
111-
startFirework(true)
177+
lastCycleFinish = TimeSource.Monotonic.markNow()
178+
lastY = player.pos.y
179+
if (pitch40ExitHeight < player.y) {
180+
controlState = ControlState.AttitudeControl
181+
speedController.reset()
182+
altitudeController.reset()
183+
}
184+
}
185+
}
186+
}
112187
}
113188
}
114189
}
190+
lastPos = player.pos
115191
}
116192

117193
onEnable {
118194
speedController.reset()
119195
altitudeController.reset()
120196
lastPos = player.pos
197+
state = Pitch40State.GainSpeed
198+
controlState = ControlState.AttitudeControl
199+
lastAngle = pitch40UpStartAngle
121200
}
122201
}
123202

@@ -143,13 +222,36 @@ object ElytraAttitudeControl : Module(
143222
}
144223
}
145224

225+
/**
226+
* Get the player's current speed in meters per second.
227+
*/
228+
fun ClientPlayerEntity.flySpeed(onlyHorizontal: Boolean = false): Float {
229+
var delta = this.pos.subtract(lastPos)
230+
if (onlyHorizontal) {
231+
delta = Vec3d(delta.x, 0.0, delta.z)
232+
}
233+
return SpeedUnit.MetersPerSecond.convertFromMinecraft(delta.length()).toFloat()
234+
}
235+
146236
enum class Mode {
147237
Speed,
148238
Altitude;
149239
}
150240

241+
enum class ControlState {
242+
AttitudeControl,
243+
Pitch40Fly
244+
}
245+
151246
enum class Group(override val displayName: String) : NamedEnum {
152247
SpeedControl("Speed Control"),
153-
AltitudeControl("Altitude Control");
248+
AltitudeControl("Altitude Control"),
249+
Pitch40Control("Pitch 40 Control"),
250+
}
251+
252+
enum class Pitch40State {
253+
GainSpeed,
254+
PitchUp,
255+
FlyUp,
154256
}
155257
}

0 commit comments

Comments
 (0)