diff --git a/src/main/java/com/lambda/mixin/render/CameraMixin.java b/src/main/java/com/lambda/mixin/render/CameraMixin.java
index 93d2f1f19..a296ece06 100644
--- a/src/main/java/com/lambda/mixin/render/CameraMixin.java
+++ b/src/main/java/com/lambda/mixin/render/CameraMixin.java
@@ -17,8 +17,9 @@
package com.lambda.mixin.render;
+import com.lambda.event.EventFlow;
+import com.lambda.event.events.CameraEvent;
import com.lambda.interaction.managers.rotating.RotationManager;
-import com.lambda.module.modules.player.Freecam;
import com.lambda.module.modules.render.CameraTweaks;
import com.lambda.module.modules.render.FreeLook;
import com.lambda.module.modules.render.NoRender;
@@ -53,9 +54,11 @@ public abstract class CameraMixin {
@Shadow
private float pitch;
- @Inject(method = "update", at = @At("TAIL"))
+ @Inject(method = "update", at = @At("TAIL"), cancellable = true)
private void onUpdate(World area, Entity focusedEntity, boolean thirdPerson, boolean inverseView, float tickProgress, CallbackInfo ci) {
- if (Freecam.INSTANCE.isEnabled()) Freecam.updateCam();
+ if (EventFlow.post(CameraEvent.CameraPosition.INSTANCE).isCanceled()) {
+ ci.cancel();
+ }
}
/**
diff --git a/src/main/kotlin/com/lambda/event/events/CameraEvent.kt b/src/main/kotlin/com/lambda/event/events/CameraEvent.kt
new file mode 100644
index 000000000..d37e48d90
--- /dev/null
+++ b/src/main/kotlin/com/lambda/event/events/CameraEvent.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 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.event.events
+
+import com.lambda.event.Event
+import com.lambda.event.callback.Cancellable
+import com.lambda.event.callback.ICancellable
+
+
+sealed class CameraEvent {
+ object CameraPosition : Event, ICancellable by Cancellable()
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/module/modules/movement/ElytraFly.kt b/src/main/kotlin/com/lambda/module/modules/movement/ElytraFly.kt
index 576bb95c3..97bf5889a 100644
--- a/src/main/kotlin/com/lambda/module/modules/movement/ElytraFly.kt
+++ b/src/main/kotlin/com/lambda/module/modules/movement/ElytraFly.kt
@@ -17,15 +17,19 @@
package com.lambda.module.modules.movement
+import com.lambda.Lambda
+import com.lambda.Lambda.mc
import com.lambda.config.AutomationConfig.Companion.setDefaultAutomationConfig
import com.lambda.config.applyEdits
import com.lambda.context.SafeContext
+import com.lambda.event.events.CameraEvent
import com.lambda.event.events.ClientEvent
import com.lambda.event.events.MovementEvent
import com.lambda.event.events.PacketEvent
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.interaction.material.StackSelection.Companion.selectStack
import com.lambda.module.Module
import com.lambda.module.modules.movement.BetterFirework.canOpenElytra
import com.lambda.module.modules.movement.BetterFirework.canTakeoff
@@ -34,11 +38,11 @@ import com.lambda.module.tag.ModuleTag
import com.lambda.threading.runSafe
import com.lambda.util.Timer
import com.lambda.util.extension.isElytraFlying
+import com.lambda.util.math.interpolate
import com.lambda.util.player.MovementUtils.addSpeed
import com.lambda.util.player.SlotUtils.hotbarAndInventoryStacks
import com.lambda.util.player.SlotUtils.hotbarStacks
import com.lambda.util.player.hasFirework
-import com.lambda.interaction.material.StackSelection.Companion.selectStack
import net.minecraft.component.DataComponentTypes
import net.minecraft.entity.Entity
import net.minecraft.item.ItemStack
@@ -77,6 +81,16 @@ object ElytraFly : Module(
private var lastDuration = 1.0
private val fireworkTimer = Timer()
+ private var stabilizeCamera = false
+ private var prevPosition: Vec3d = Vec3d.ZERO
+ private var position: Vec3d = Vec3d.ZERO
+ private val flipFlopLerpPos: Vec3d
+ get() {
+ var tickProgress = mc.gameRenderer.camera.lastTickProgress / 2
+ if (!flipFlop) tickProgress += 0.5f
+ return prevPosition.interpolate(tickProgress, position)
+ }
+
init {
setDefaultAutomationConfig {
applyEdits {
@@ -84,6 +98,18 @@ object ElytraFly : Module(
}
}
+ onEnable {
+ position = player.eyePos
+ prevPosition = position
+ }
+
+ listen {
+ if (mode != FlyMode.GrimControl || !stabilizeCamera) return@listen
+ Lambda.mc.gameRenderer.apply {
+ camera.setPos(flipFlopLerpPos.x, flipFlopLerpPos.y, flipFlopLerpPos.z)
+ }
+ }
+
listen {
if (mode == FlyMode.GrimControl) onTickGrimControl()
else if (mode == FlyMode.Bounce) onTickBounce()
@@ -132,22 +158,33 @@ object ElytraFly : Module(
var vec = Vec3d.ZERO
val yaw = player.yaw
- if (mc.options.forwardKey.isPressed) vec = vec.add(Vec3d.fromPolar(0f, yaw))
- if (mc.options.backKey.isPressed) vec = vec.add(Vec3d.fromPolar(0f, yaw + 180f))
- if (mc.options.leftKey.isPressed) vec = vec.add(Vec3d.fromPolar(0f, yaw - 90f))
- if (mc.options.rightKey.isPressed) vec = vec.add(Vec3d.fromPolar(0f, yaw + 90f))
- if (mc.options.jumpKey.isPressed) vec = vec.add(Vec3d(0.0, 1.0, 0.0))
- if (mc.options.sneakKey.isPressed) vec = vec.add(Vec3d(0.0, -1.0, 0.0))
- if (vec.lengthSquared() < 1e-4 && player.hasFirework) {
+ if (flipFlop) {
+ if (mc.options.forwardKey.isPressed) vec = vec.add(Vec3d.fromPolar(0f, yaw))
+ if (mc.options.backKey.isPressed) vec = vec.add(Vec3d.fromPolar(0f, yaw + 180f))
+ if (mc.options.leftKey.isPressed) vec = vec.add(Vec3d.fromPolar(0f, yaw - 90f))
+ if (mc.options.rightKey.isPressed) vec = vec.add(Vec3d.fromPolar(0f, yaw + 90f))
+ if (mc.options.jumpKey.isPressed) vec = vec.add(Vec3d(0.0, 1.0, 0.0))
+ if (mc.options.sneakKey.isPressed) vec = vec.add(Vec3d(0.0, -1.0, 0.0))
+ }
+ if (vec.lengthSquared() < 1e-2 && player.hasFirework) {
+ if (!stabilizeCamera) {
+ stabilizeCamera = true
+ position = player.eyePos
+ prevPosition = position
+ flipFlop = true
+ }
if (flipFlop) {
flipFlop = false
rotationRequest { rotation(0f, 0f) }
} else {
flipFlop = true
+ prevPosition = position
+ position = player.eyePos
rotationRequest { rotation(180f, 0f) }
}
} else {
val rot = vec.yawAndPitch
+ stabilizeCamera = false
rotationRequest { rotation(rot.y, rot.x) }
}.submit()
}
diff --git a/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt b/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt
index 4c0558313..181703a7d 100644
--- a/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt
+++ b/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt
@@ -17,7 +17,9 @@
package com.lambda.module.modules.player
+import com.lambda.Lambda
import com.lambda.Lambda.mc
+import com.lambda.event.events.CameraEvent
import com.lambda.event.events.MovementEvent
import com.lambda.event.events.PlayerEvent
import com.lambda.event.events.RenderEvent
@@ -84,14 +86,6 @@ object Freecam : Module(
private var rotation: Rotation = Rotation.ZERO
private var velocity: Vec3d = Vec3d.ZERO
- @JvmStatic
- fun updateCam() {
- mc.gameRenderer.apply {
- camera.setRotation(rotation.yawF, rotation.pitchF)
- camera.setPos(lerpPos.x, lerpPos.y, lerpPos.z)
- }
- }
-
/**
* @see net.minecraft.entity.Entity.changeLookDirection
*/
@@ -110,6 +104,13 @@ object Freecam : Module(
mc.options.perspective = lastPerspective
}
+ listen {
+ Lambda.mc.gameRenderer.apply {
+ camera.setRotation(rotation.yawF, rotation.pitchF)
+ camera.setPos(lerpPos.x, lerpPos.y, lerpPos.z)
+ }
+ }
+
listen {
when (rotateMode) {
FreecamRotationMode.None -> return@listen