diff --git a/src/main/java/com/lambda/mixin/entity/EntityMixin.java b/src/main/java/com/lambda/mixin/entity/EntityMixin.java
index 133d41280..8bd0ada26 100644
--- a/src/main/java/com/lambda/mixin/entity/EntityMixin.java
+++ b/src/main/java/com/lambda/mixin/entity/EntityMixin.java
@@ -22,9 +22,12 @@
import com.lambda.event.events.EntityEvent;
import com.lambda.event.events.PlayerEvent;
import com.lambda.interaction.request.rotating.RotationManager;
+import com.lambda.interaction.request.rotating.RotationMode;
+import com.lambda.module.modules.player.RotationLock;
import com.lambda.module.modules.render.NoRender;
import com.lambda.util.math.Vec2d;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
import net.minecraft.entity.Entity;
import net.minecraft.entity.MovementType;
import net.minecraft.entity.data.TrackedData;
@@ -142,4 +145,20 @@ private boolean modifyGetFlagInvisible(boolean original) {
private boolean modifyGetFlagGlowing(boolean original) {
return (NoRender.INSTANCE.isDisabled() || !NoRender.getNoGlow()) && original;
}
+
+ @WrapWithCondition(method = "changeLookDirection", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;setYaw(F)V"))
+ private boolean wrapSetYaw(Entity instance, float yaw) {
+ return (instance != Lambda.getMc().player ||
+ RotationLock.INSTANCE.isDisabled() ||
+ RotationLock.getRotationSettings().getRotationMode() != RotationMode.Lock ||
+ RotationLock.getYawMode() == RotationLock.RotationMode.None);
+ }
+
+ @WrapWithCondition(method = "changeLookDirection", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;setPitch(F)V"))
+ private boolean wrapSetPitch(Entity instance, float yaw) {
+ return (instance != Lambda.getMc().player ||
+ RotationLock.INSTANCE.isDisabled() ||
+ RotationLock.getRotationSettings().getRotationMode() != RotationMode.Lock ||
+ RotationLock.getPitchMode() == RotationLock.RotationMode.None);
+ }
}
diff --git a/src/main/kotlin/com/lambda/module/modules/player/RotationLock.kt b/src/main/kotlin/com/lambda/module/modules/player/RotationLock.kt
new file mode 100644
index 000000000..2d59eac94
--- /dev/null
+++ b/src/main/kotlin/com/lambda/module/modules/player/RotationLock.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.player
+
+import com.lambda.config.groups.RotationSettings
+import com.lambda.event.events.TickEvent
+import com.lambda.event.listener.SafeListener.Companion.listen
+import com.lambda.interaction.request.rotating.Rotation
+import com.lambda.interaction.request.rotating.RotationRequest
+import com.lambda.interaction.request.rotating.visibilty.lookAt
+import com.lambda.module.Module
+import com.lambda.module.tag.ModuleTag
+import com.lambda.util.NamedEnum
+import kotlin.math.roundToInt
+
+object RotationLock : Module(
+ name = "RotationLock",
+ description = "Locks the player rotation to the given configuration",
+ tag = ModuleTag.PLAYER,
+) {
+ private enum class Group(override val displayName: String) : NamedEnum {
+ General("General"),
+ Rotation("Rotation")
+ }
+
+ @JvmStatic val yawMode by setting("Yaw Mode", RotationMode.Snap).group(Group.General)
+ private val yawStep by setting("Yaw Step", 45.0, 1.0..180.0, 1.0) { yawMode == RotationMode.Snap }.group(Group.General)
+ private val customYaw by setting("Custom Yaw", 0.0, -179.0..180.0, 1.0) { yawMode == RotationMode.Custom }.group(Group.General)
+ @JvmStatic val pitchMode by setting("Pitch Mode", RotationMode.Custom).group(Group.General)
+ private val pitchStep by setting("Pitch Step", 45.0, 1.0..90.0, 1.0) { pitchMode == RotationMode.Snap }.group(Group.General)
+ private val customPitch by setting("Custom Pitch", 0.0, -90.0..90.0, 1.0) { pitchMode == RotationMode.Custom }.group(Group.General)
+
+ @JvmStatic val rotationSettings = RotationSettings(this, Group.Rotation)
+ private var rotationRequest: RotationRequest? = null
+
+ init {
+ listen {
+ val yaw = when (yawMode) {
+ RotationMode.Custom -> customYaw
+ RotationMode.Snap -> {
+ val normalizedYaw = (player.yaw % 360.0 + 360.0) % 360.0
+ (normalizedYaw / yawStep).roundToInt() * yawStep
+ }
+ RotationMode.None -> player.yaw.toDouble()
+ }
+ val pitch = when (pitchMode) {
+ RotationMode.Custom -> customPitch
+ RotationMode.Snap -> {
+ val clampedPitch = player.pitch.coerceIn(-90f, 90f)
+ (clampedPitch / pitchStep).roundToInt() * pitchStep
+ }
+ RotationMode.None -> player.pitch.toDouble()
+ }
+
+ RotationRequest(lookAt(Rotation(yaw, pitch), 0.001), rotationSettings).submit()
+ }
+ }
+
+ enum class RotationMode {
+ Snap,
+ Custom,
+ None
+ }
+}