From 5b18d9e8a762840fdb1fb1dee1a3118cb5daf670 Mon Sep 17 00:00:00 2001 From: oblongboot <81798709+oblongboot@users.noreply.github.com> Date: Wed, 1 Apr 2026 20:28:32 +0100 Subject: [PATCH 1/2] feat: rotation interface --- src/main/kotlin/org/cobalt/Cobalt.kt | 3 +- .../org/cobalt/command/impl/MainCommand.kt | 11 +++ .../cobalt/util/rotation/DefaultRotations.kt | 99 +++++++++++++++++++ .../org/cobalt/util/rotation/IRotation.kt | 9 ++ .../cobalt/util/rotation/RotationHandler.kt | 12 +++ .../cobalt/util/rotation/RotationManager.kt | 29 ++++++ 6 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/org/cobalt/util/rotation/DefaultRotations.kt create mode 100644 src/main/kotlin/org/cobalt/util/rotation/IRotation.kt create mode 100644 src/main/kotlin/org/cobalt/util/rotation/RotationHandler.kt create mode 100644 src/main/kotlin/org/cobalt/util/rotation/RotationManager.kt diff --git a/src/main/kotlin/org/cobalt/Cobalt.kt b/src/main/kotlin/org/cobalt/Cobalt.kt index 8b829e6..16a52ea 100644 --- a/src/main/kotlin/org/cobalt/Cobalt.kt +++ b/src/main/kotlin/org/cobalt/Cobalt.kt @@ -10,6 +10,7 @@ import org.cobalt.command.impl.MainCommand import org.cobalt.event.EventBus import org.cobalt.event.impl.WorldRenderEvent import org.cobalt.module.ModuleManager +import org.cobalt.util.rotation.RotationHandler object Cobalt : ClientModInitializer { @@ -28,7 +29,7 @@ object Cobalt : ClientModInitializer { override fun onInitializeClient() { ModuleManager.registerModules() CommandManager.register(MainCommand) - + EventBus.register(RotationHandler) // Dispatch Events LevelRenderEvents.END_MAIN.register { context -> EventBus.post(WorldRenderEvent(context)) diff --git a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt index 6d55979..a869f03 100644 --- a/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt +++ b/src/main/kotlin/org/cobalt/command/impl/MainCommand.kt @@ -3,9 +3,12 @@ package org.cobalt.command.impl import net.minecraft.client.Minecraft import org.cobalt.command.Command import org.cobalt.command.annotation.DefaultHandler +import org.cobalt.command.annotation.SubCommand import org.cobalt.ui.screen.ConfigScreen import org.cobalt.util.ChatUtils import org.cobalt.util.helper.TickScheduler +import org.cobalt.util.rotation.DefaultRotations +import org.cobalt.util.rotation.RotationManager internal object MainCommand : Command( name = "cobalt", @@ -22,4 +25,12 @@ internal object MainCommand : Command( } } + @SubCommand + fun rotate(yaw: Double, pitch: Double) { + RotationManager.setActiveRotation( + DefaultRotations, + yaw = yaw, + pitch = pitch + ) + } } diff --git a/src/main/kotlin/org/cobalt/util/rotation/DefaultRotations.kt b/src/main/kotlin/org/cobalt/util/rotation/DefaultRotations.kt new file mode 100644 index 0000000..7b1c535 --- /dev/null +++ b/src/main/kotlin/org/cobalt/util/rotation/DefaultRotations.kt @@ -0,0 +1,99 @@ +package org.cobalt.util.rotation + +import org.cobalt.util.ChatUtils +import org.cobalt.util.MessageType +import kotlin.math.abs +import org.cobalt.Cobalt.mc + +object DefaultRotations : IRotation { + private var rotating = false + private var targetYaw = 0.0 + private var targetPitch = 0.0 + private var currentYaw = 0.0 + private var currentPitch = 0.0 + + override fun onRotationStart(yaw: Double, pitch: Double) { + rotating = true + targetYaw = yaw + targetPitch = pitch + currentYaw = getPlayerYaw() + currentPitch = getPlayerPitch() + + ChatUtils.message("Rotation started to $yaw, $pitch", MessageType.DEBUG) + } + + override fun onRotationEnd() { + rotating = false + ChatUtils.message("Ended rotation.", MessageType.DEBUG) + } + + override fun onRotationWorldRender() { + if (!rotating) return + + val player = getPlayer() ?: return + + val currentYaw = player.yRot.toDouble() + val currentPitch = player.xRot.toDouble() + + val speed = 0.15 // TODO: add this as param in function + + val newYaw = lerpAngle(currentYaw, targetYaw, speed) + val newPitch = lerp(currentPitch, targetPitch, speed) + + applyRotation(newYaw, newPitch) + + if (distance(newYaw, targetYaw) < 0.05 && + kotlin.math.abs(newPitch - targetPitch) < 0.05 + ) { + stopRotation() + } + } + + private fun lerpAngle(current: Double, target: Double, alpha: Double): Double { + var delta = ((target - current + 540) % 360) - 180 + return current + delta * alpha + } + // YES its lerp, do I care? no! ill change in my next commit. + private fun lerp(a: Double, b: Double, t: Double): Double { + return a + (b - a) * t + } + + private fun distance(a: Double, b: Double): Double { + return kotlin.math.abs(a - b) + } + + override fun isRotating(): Boolean = rotating + + private fun applyRotation(yaw: Double, pitch: Double) { + val player = getPlayer() ?: return + + val y = yaw.toFloat() + val p = pitch.toFloat() + + player.yRot = y + player.xRot = p + + player.yRotO = y + player.xRotO = p + + player.yHeadRot = y + player.yBodyRot = y + } + + private fun wrapAngle(angle: Double): Double { + var a = angle % 360.0 + if (a >= 180) a -= 360.0 + if (a < -180) a += 360.0 + return a + } + + private fun getPlayerYaw(): Double { + return getPlayer()?.yRot?.toDouble() ?: 0.0 + } + + private fun getPlayerPitch(): Double { + return getPlayer()?.xRot?.toDouble() ?: 0.0 + } + + private fun getPlayer() = mc.player +} diff --git a/src/main/kotlin/org/cobalt/util/rotation/IRotation.kt b/src/main/kotlin/org/cobalt/util/rotation/IRotation.kt new file mode 100644 index 0000000..7c1078d --- /dev/null +++ b/src/main/kotlin/org/cobalt/util/rotation/IRotation.kt @@ -0,0 +1,9 @@ +package org.cobalt.util.rotation + +interface IRotation { + fun onRotationWorldRender() + fun onRotationEnd() + fun onRotationStart(yaw: Double, pitch: Double) + fun isRotating(): Boolean + fun stopRotation() { onRotationEnd() } +} diff --git a/src/main/kotlin/org/cobalt/util/rotation/RotationHandler.kt b/src/main/kotlin/org/cobalt/util/rotation/RotationHandler.kt new file mode 100644 index 0000000..f2c2c59 --- /dev/null +++ b/src/main/kotlin/org/cobalt/util/rotation/RotationHandler.kt @@ -0,0 +1,12 @@ +package org.cobalt.util.rotation + +import org.cobalt.event.annotation.SubscribeEvent +import org.cobalt.event.impl.WorldRenderEvent + +internal object RotationHandler { + @SubscribeEvent + fun onWorldRender(event: WorldRenderEvent) { + if (!RotationManager.getActiveRotation().isRotating()) return + RotationManager.getActiveRotation().onRotationWorldRender() + } +} diff --git a/src/main/kotlin/org/cobalt/util/rotation/RotationManager.kt b/src/main/kotlin/org/cobalt/util/rotation/RotationManager.kt new file mode 100644 index 0000000..0c8e68d --- /dev/null +++ b/src/main/kotlin/org/cobalt/util/rotation/RotationManager.kt @@ -0,0 +1,29 @@ +package org.cobalt.util.rotation + +object RotationManager { + private var rotation: IRotation = DefaultRotations + + fun setActiveRotation(newRotation: IRotation, yaw: Double, pitch: Double) { + if (rotation.isRotating()) { + rotation.stopRotation() + } + + rotation = newRotation + rotation.onRotationStart(yaw, pitch) + } + + fun getActiveRotation(): IRotation = rotation + + fun resetRotation() { + if (rotation.isRotating()) { + rotation.stopRotation() + } + rotation = DefaultRotations + } + + fun onWorldRender() { + if (rotation.isRotating()) { + rotation.onRotationWorldRender() + } + } +} From 9ce63c398fc87f3ff4165fbe52dc0f440beb5610 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:33:11 -0400 Subject: [PATCH 2/2] chore: cleanup --- src/main/kotlin/org/cobalt/Cobalt.kt | 3 +-- .../cobalt/util/rotation/DefaultRotations.kt | 19 ++++++++----------- .../org/cobalt/util/rotation/IRotation.kt | 7 ++++++- .../cobalt/util/rotation/RotationHandler.kt | 12 ------------ .../cobalt/util/rotation/RotationManager.kt | 19 +++++++++++++++---- 5 files changed, 30 insertions(+), 30 deletions(-) delete mode 100644 src/main/kotlin/org/cobalt/util/rotation/RotationHandler.kt diff --git a/src/main/kotlin/org/cobalt/Cobalt.kt b/src/main/kotlin/org/cobalt/Cobalt.kt index 16a52ea..8b829e6 100644 --- a/src/main/kotlin/org/cobalt/Cobalt.kt +++ b/src/main/kotlin/org/cobalt/Cobalt.kt @@ -10,7 +10,6 @@ import org.cobalt.command.impl.MainCommand import org.cobalt.event.EventBus import org.cobalt.event.impl.WorldRenderEvent import org.cobalt.module.ModuleManager -import org.cobalt.util.rotation.RotationHandler object Cobalt : ClientModInitializer { @@ -29,7 +28,7 @@ object Cobalt : ClientModInitializer { override fun onInitializeClient() { ModuleManager.registerModules() CommandManager.register(MainCommand) - EventBus.register(RotationHandler) + // Dispatch Events LevelRenderEvents.END_MAIN.register { context -> EventBus.post(WorldRenderEvent(context)) diff --git a/src/main/kotlin/org/cobalt/util/rotation/DefaultRotations.kt b/src/main/kotlin/org/cobalt/util/rotation/DefaultRotations.kt index 7b1c535..bc3ff4e 100644 --- a/src/main/kotlin/org/cobalt/util/rotation/DefaultRotations.kt +++ b/src/main/kotlin/org/cobalt/util/rotation/DefaultRotations.kt @@ -6,6 +6,7 @@ import kotlin.math.abs import org.cobalt.Cobalt.mc object DefaultRotations : IRotation { + private var rotating = false private var targetYaw = 0.0 private var targetPitch = 0.0 @@ -42,24 +43,26 @@ object DefaultRotations : IRotation { applyRotation(newYaw, newPitch) - if (distance(newYaw, targetYaw) < 0.05 && - kotlin.math.abs(newPitch - targetPitch) < 0.05 + if ( + distance(newYaw, targetYaw) < 0.05 && + abs(newPitch - targetPitch) < 0.05 ) { stopRotation() } } private fun lerpAngle(current: Double, target: Double, alpha: Double): Double { - var delta = ((target - current + 540) % 360) - 180 + val delta = ((target - current + 540) % 360) - 180 return current + delta * alpha } + // YES its lerp, do I care? no! ill change in my next commit. private fun lerp(a: Double, b: Double, t: Double): Double { return a + (b - a) * t } private fun distance(a: Double, b: Double): Double { - return kotlin.math.abs(a - b) + return abs(a - b) } override fun isRotating(): Boolean = rotating @@ -80,13 +83,6 @@ object DefaultRotations : IRotation { player.yBodyRot = y } - private fun wrapAngle(angle: Double): Double { - var a = angle % 360.0 - if (a >= 180) a -= 360.0 - if (a < -180) a += 360.0 - return a - } - private fun getPlayerYaw(): Double { return getPlayer()?.yRot?.toDouble() ?: 0.0 } @@ -96,4 +92,5 @@ object DefaultRotations : IRotation { } private fun getPlayer() = mc.player + } diff --git a/src/main/kotlin/org/cobalt/util/rotation/IRotation.kt b/src/main/kotlin/org/cobalt/util/rotation/IRotation.kt index 7c1078d..5863340 100644 --- a/src/main/kotlin/org/cobalt/util/rotation/IRotation.kt +++ b/src/main/kotlin/org/cobalt/util/rotation/IRotation.kt @@ -1,9 +1,14 @@ package org.cobalt.util.rotation interface IRotation { + fun onRotationWorldRender() fun onRotationEnd() fun onRotationStart(yaw: Double, pitch: Double) fun isRotating(): Boolean - fun stopRotation() { onRotationEnd() } + + fun stopRotation() { + onRotationEnd() + } + } diff --git a/src/main/kotlin/org/cobalt/util/rotation/RotationHandler.kt b/src/main/kotlin/org/cobalt/util/rotation/RotationHandler.kt deleted file mode 100644 index f2c2c59..0000000 --- a/src/main/kotlin/org/cobalt/util/rotation/RotationHandler.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.cobalt.util.rotation - -import org.cobalt.event.annotation.SubscribeEvent -import org.cobalt.event.impl.WorldRenderEvent - -internal object RotationHandler { - @SubscribeEvent - fun onWorldRender(event: WorldRenderEvent) { - if (!RotationManager.getActiveRotation().isRotating()) return - RotationManager.getActiveRotation().onRotationWorldRender() - } -} diff --git a/src/main/kotlin/org/cobalt/util/rotation/RotationManager.kt b/src/main/kotlin/org/cobalt/util/rotation/RotationManager.kt index 0c8e68d..7f60576 100644 --- a/src/main/kotlin/org/cobalt/util/rotation/RotationManager.kt +++ b/src/main/kotlin/org/cobalt/util/rotation/RotationManager.kt @@ -1,8 +1,17 @@ package org.cobalt.util.rotation +import org.cobalt.event.EventBus +import org.cobalt.event.annotation.SubscribeEvent +import org.cobalt.event.impl.WorldRenderEvent + object RotationManager { + private var rotation: IRotation = DefaultRotations + init { + EventBus.register(this) + } + fun setActiveRotation(newRotation: IRotation, yaw: Double, pitch: Double) { if (rotation.isRotating()) { rotation.stopRotation() @@ -18,12 +27,14 @@ object RotationManager { if (rotation.isRotating()) { rotation.stopRotation() } + rotation = DefaultRotations } - fun onWorldRender() { - if (rotation.isRotating()) { - rotation.onRotationWorldRender() - } + @SubscribeEvent + fun onWorldRender(event: WorldRenderEvent) { + if (!rotation.isRotating()) return + rotation.onRotationWorldRender() } + }