diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e5d4c751..8bace745 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -100,4 +100,4 @@ jobs: modrinth-token: ${{ secrets.MODRINTH_TOKEN }} curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} - github-token: ${{ secrets.TOKEN_GITHUB }} + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 33103032..d4ea4ac3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,2 @@ -- Added the ability to switch the yaw and roll axes. -- Added sensitivity settings. -- Added controller compat via MidnightControls. \ No newline at end of file +- Fixed camera drifting while game is paused (#5) +- Added momentum based camera movement option \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 1210bab3..677e3240 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,8 @@ minecraft_version=1.19 yarn_mappings=1.19+build.4 loader_version=0.14.8 -release_fancyname = 1.1.0 for 1.19.x -mod_version = 1.1.0+1.19 +release_fancyname = 1.2.0 for 1.19.x +mod_version = 1.2.0+1.19 maven_group = nl.enjarai archives_base_name = do-a-barrel-roll diff --git a/src/main/java/nl/enjarai/doabarrelroll/DoABarrelRollClient.java b/src/main/java/nl/enjarai/doabarrelroll/DoABarrelRollClient.java index cd321daa..badcb352 100644 --- a/src/main/java/nl/enjarai/doabarrelroll/DoABarrelRollClient.java +++ b/src/main/java/nl/enjarai/doabarrelroll/DoABarrelRollClient.java @@ -8,6 +8,7 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec2f; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3f; import nl.enjarai.doabarrelroll.config.ModConfig; @@ -23,6 +24,7 @@ public class DoABarrelRollClient implements ClientModInitializer { private static double lastLerpUpdate; public static double landingLerp = 1; public static Vec3d left; + public static Vec2f mouseTurnVec = Vec2f.ZERO; @Override @@ -43,70 +45,126 @@ public static void updateMouse(ClientPlayerEntity player, double cursorDeltaX, d // smoothly lerp left vector to the assumed upright left if not in flight if (!player.isFallFlying()) { + landingLerp = MathHelper.lerp(MathHelper.clamp(lerpDelta * 2, 0, 1), landingLerp, 1); // round the lerp off when done to hopefully avoid world flickering if (landingLerp > 0.9) landingLerp = 1; - clearSmoothers(); + clearValues(); player.changeLookDirection(cursorDeltaX, cursorDeltaY); left = left.lerp(ElytraMath.getAssumedLeft(player.getYaw()), landingLerp); + return; } + + // reset the landing animation when flying landingLerp = 0; - changeElytraLook(cursorDeltaY, 0, cursorDeltaX, ModConfig.INSTANCE.desktopSensitivity); + if (ModConfig.INSTANCE.momentumBasedMouse) { + + // add the mouse movement to the current vector and normalize if needed + var turnVec = mouseTurnVec.add(new Vec2f((float) cursorDeltaX, (float) cursorDeltaY).multiply(1f / 300)); + if (turnVec.lengthSquared() > 1) { + turnVec = turnVec.normalize(); + } + mouseTurnVec = turnVec; + + // enlarge the vector and apply it to the camera + var delta = getDelta(); + var readyTurnVec = mouseTurnVec.multiply(1200 * (float) delta); + changeElytraLook(readyTurnVec.y, 0, readyTurnVec.x, ModConfig.INSTANCE.desktopSensitivity, delta); + + } else { + + // if we are not using a momentum based mouse, we can reset it and apply the values directly + mouseTurnVec = Vec2f.ZERO; + changeElytraLook(cursorDeltaY, 0, cursorDeltaX, ModConfig.INSTANCE.desktopSensitivity); + } } public static void onWorldRender(MinecraftClient client, float tickDelta, long limitTime, MatrixStack matrix) { if (client.player == null || !client.player.isFallFlying()) { - clearSmoothers(); + clearValues(); } else { - var yawDelta = 25f; - var yaw = 0; + if (client.isPaused()) { - // Strafe buttons - if (client.options.leftKey.isPressed()) { - yaw -= yawDelta; - } - if (client.options.rightKey.isPressed()) { - yaw += yawDelta; - } + // keep updating the last look update time when paused to prevent large jumps after unpausing + lastLookUpdate = GlfwUtil.getTime(); - changeElytraLook(0, yaw, 0, ModConfig.INSTANCE.desktopSensitivity); + } else { + var yawDelta = 25f; + var yaw = 0; + + // Strafe buttons + if (client.options.leftKey.isPressed()) { + yaw -= yawDelta; + } + if (client.options.rightKey.isPressed()) { + yaw += yawDelta; + } + + changeElytraLook(0, yaw, 0, ModConfig.INSTANCE.desktopSensitivity); + } } if (landingLerp < 1) { + // calculate the camera angle and apply it double angle = -Math.acos(left.dotProduct(ElytraMath.getAssumedLeft(client.player.getYaw()))) * ElytraMath.TODEG; if (left.getY() < 0) angle *= -1; - matrix.multiply(Vec3f.POSITIVE_Z.getDegreesQuaternion((float) angle)); } } + public static void onRenderCrosshair(MatrixStack matrices, int scaledWidth, int scaledHeight) { + if (!DoABarrelRollClient.isFallFlying() || !ModConfig.INSTANCE.momentumBasedMouse || !ModConfig.INSTANCE.showMomentumWidget) return; + + MomentumCrosshairWidget.render(matrices, scaledWidth, scaledHeight, mouseTurnVec); + } + - private static void clearSmoothers() { + private static void clearValues() { pitchSmoother.clear(); yawSmoother.clear(); rollSmoother.clear(); + mouseTurnVec = Vec2f.ZERO; } - public static void changeElytraLook(double pitch, double yaw, double roll, Sensitivity sensitivity) { - var player = MinecraftClient.getInstance().player; - if (player == null) return; - - // calculate time since last update + /** + * Returns the time since the last look update. + * + *

+ * Only call if you're going to call + * {@link DoABarrelRollClient#changeElytraLook(double, double, double, Sensitivity, double)} + * right after this using the returned value. + * Neglecting to do this will disrupt the smoothness of the camera. + *

+ */ + private static double getDelta() { double time = GlfwUtil.getTime(); double delta = time - lastLookUpdate; lastLookUpdate = time; + return delta; + } + + /** + * Only use if you haven't called {@link DoABarrelRollClient#getDelta()} before this. + */ + public static void changeElytraLook(double pitch, double yaw, double roll, Sensitivity sensitivity) { + changeElytraLook(pitch, yaw, roll, sensitivity, getDelta()); + } + + public static void changeElytraLook(double pitch, double yaw, double roll, Sensitivity sensitivity, double delta) { + var player = MinecraftClient.getInstance().player; + if (player == null) return; // smooth the look changes pitch = pitchSmoother.smooth(pitch, sensitivity.pitch * delta); diff --git a/src/main/java/nl/enjarai/doabarrelroll/ModMath.java b/src/main/java/nl/enjarai/doabarrelroll/ModMath.java new file mode 100644 index 00000000..71b40f52 --- /dev/null +++ b/src/main/java/nl/enjarai/doabarrelroll/ModMath.java @@ -0,0 +1,37 @@ +package nl.enjarai.doabarrelroll; + +import java.util.function.BiConsumer; + +public class ModMath { + + public static void forBresenhamLine(int x0, int y0, int x1, int y1, BiConsumer consumer) { + + int dx = Math.abs(x1 - x0); + int dy = Math.abs(y1 - y0); + + int sx = x0 < x1 ? 1 : -1; + int sy = y0 < y1 ? 1 : -1; + + int err = dx - dy; + int e2; + + while (true) { + + consumer.accept(x0, y0); + + if (x0 == x1 && y0 == y1) break; + + e2 = 2 * err; + + if (e2 > -dy) { + err = err - dy; + x0 = x0 + sx; + } + + if (e2 < dx) { + err = err + dx; + y0 = y0 + sy; + } + } + } +} diff --git a/src/main/java/nl/enjarai/doabarrelroll/MomentumCrosshairWidget.java b/src/main/java/nl/enjarai/doabarrelroll/MomentumCrosshairWidget.java new file mode 100644 index 00000000..2634bba9 --- /dev/null +++ b/src/main/java/nl/enjarai/doabarrelroll/MomentumCrosshairWidget.java @@ -0,0 +1,43 @@ +package nl.enjarai.doabarrelroll; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.render.*; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.Vec2f; + +public class MomentumCrosshairWidget { + + public static void render(MatrixStack matrices, int scaledWidth, int scaledHeight, Vec2f mouseTurnVec) { + int color = 0xffffffff; + int centerX = scaledWidth / 2; + int centerY = scaledHeight / 2 - 1; + var turnVec = mouseTurnVec.multiply(50); + var lineVec = turnVec.add(turnVec.negate().normalize().multiply(Math.min(turnVec.length(), 10f))); + + if (!lineVec.equals(Vec2f.ZERO)) { + + RenderSystem.enableBlend(); + RenderSystem.disableTexture(); + RenderSystem.blendFuncSeparate(GlStateManager.SrcFactor.ONE_MINUS_DST_COLOR, GlStateManager.DstFactor.ONE_MINUS_SRC_COLOR, GlStateManager.SrcFactor.ONE, GlStateManager.DstFactor.ZERO); + RenderSystem.setShader(GameRenderer::getPositionColorShader); + ModMath.forBresenhamLine(centerX, centerY, centerX + (int) lineVec.x, centerY + (int) lineVec.y, (x, y) -> { + + var matrix = matrices.peek().getPositionMatrix(); + var bufferBuilder = Tessellator.getInstance().getBuffer(); + + bufferBuilder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR); + bufferBuilder.vertex(matrix, (float) x, (float) y + 1, 0.0F).color(color).next(); + bufferBuilder.vertex(matrix, (float) x + 1, (float) y + 1, 0.0F).color(color).next(); + bufferBuilder.vertex(matrix, (float) x + 1, (float) y, 0.0F).color(color).next(); + bufferBuilder.vertex(matrix, (float) x, (float) y, 0.0F).color(color).next(); + BufferRenderer.drawWithShader(bufferBuilder.end()); + + }); + RenderSystem.enableTexture(); + } + + // change the position of the crosshair, which is rendered up the stack + matrices.translate((int) turnVec.x, (int) turnVec.y, 0); + } +} diff --git a/src/main/java/nl/enjarai/doabarrelroll/config/ModConfig.java b/src/main/java/nl/enjarai/doabarrelroll/config/ModConfig.java index 4ca0e186..0b483011 100644 --- a/src/main/java/nl/enjarai/doabarrelroll/config/ModConfig.java +++ b/src/main/java/nl/enjarai/doabarrelroll/config/ModConfig.java @@ -9,6 +9,7 @@ @Config(name = DoABarrelRollClient.MODID) public class ModConfig implements ConfigData { + @ConfigEntry.Gui.Excluded public static ModConfig INSTANCE; public static void init() { @@ -19,6 +20,12 @@ public static void init() { @ConfigEntry.Gui.Tooltip public boolean switchRollAndYaw = false; + @ConfigEntry.Gui.Tooltip + public boolean momentumBasedMouse = false; + + @ConfigEntry.Gui.Tooltip + public boolean showMomentumWidget = true; + @ConfigEntry.Gui.CollapsibleObject public Sensitivity desktopSensitivity = new Sensitivity(1, 0.4, 1); diff --git a/src/main/java/nl/enjarai/doabarrelroll/mixin/InGameHudMixin.java b/src/main/java/nl/enjarai/doabarrelroll/mixin/InGameHudMixin.java new file mode 100644 index 00000000..24e63e1f --- /dev/null +++ b/src/main/java/nl/enjarai/doabarrelroll/mixin/InGameHudMixin.java @@ -0,0 +1,37 @@ +package nl.enjarai.doabarrelroll.mixin; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gui.DrawableHelper; +import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.client.util.math.MatrixStack; +import nl.enjarai.doabarrelroll.DoABarrelRollClient; +import nl.enjarai.doabarrelroll.ElytraMath; +import nl.enjarai.doabarrelroll.ModMath; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(InGameHud.class) +public abstract class InGameHudMixin extends DrawableHelper { + + @Shadow private int scaledWidth; + @Shadow private int scaledHeight; + + @Shadow abstract void renderCrosshair(MatrixStack matrices); + + @Redirect( + method = "render(Lnet/minecraft/client/util/math/MatrixStack;F)V", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/hud/InGameHud;renderCrosshair(Lnet/minecraft/client/util/math/MatrixStack;)V" + ) + ) + private void onRenderCrosshair(InGameHud inGameHud, MatrixStack matrixStack) { + matrixStack.push(); + DoABarrelRollClient.onRenderCrosshair(matrixStack, scaledWidth, scaledHeight); + renderCrosshair(matrixStack); + matrixStack.pop(); + } +} diff --git a/src/main/java/nl/enjarai/doabarrelroll/mixin/MouseMixin.java b/src/main/java/nl/enjarai/doabarrelroll/mixin/MouseMixin.java index 72ec5a2b..2d379020 100644 --- a/src/main/java/nl/enjarai/doabarrelroll/mixin/MouseMixin.java +++ b/src/main/java/nl/enjarai/doabarrelroll/mixin/MouseMixin.java @@ -10,50 +10,6 @@ @Mixin(Mouse.class) public abstract class MouseMixin { -// @Shadow @Final private SmoothUtil cursorXSmoother; -// @Shadow @Final private SmoothUtil cursorYSmoother; -// -// -// // force enable the smooth camera while flying -// @Redirect( -// method = "updateMouse", -// at = @At( -// value = "FIELD", -// target = "Lnet/minecraft/client/option/GameOptions;smoothCameraEnabled:Z" -// ) -// ) -// private boolean smoothCameraEnabled(GameOptions options) { -// return options.smoothCameraEnabled || DoABarrelRollClient.shouldSmooth(); -// } -// -// -// // use our own smoothing while in flight -// -// // X axis -// @Redirect( -// method = "updateMouse", -// at = @At( -// value = "FIELD", -// target = "Lnet/minecraft/client/Mouse;cursorXSmoother:Lnet/minecraft/client/util/SmoothUtil;" -// ) -// ) -// private SmoothUtil modXSmoother(Mouse thiz) { -// return DoABarrelRollClient.shouldSmooth() ? DoABarrelRollClient.rollSmoother : cursorXSmoother; -// } -// -// // Y axis -// @Redirect( -// method = "updateMouse", -// at = @At( -// value = "FIELD", -// target = "Lnet/minecraft/client/Mouse;cursorYSmoother:Lnet/minecraft/client/util/SmoothUtil;" -// ) -// ) -// private SmoothUtil modYSmoother(Mouse thiz) { -// return DoABarrelRollClient.shouldSmooth() ? DoABarrelRollClient.pitchSmoother : cursorYSmoother; -// } - - // redirect mouse handling to our own code @Redirect( method = "updateMouse", diff --git a/src/main/resources/assets/do-a-barrel-roll/lang/en_us.json b/src/main/resources/assets/do-a-barrel-roll/lang/en_us.json index e17f1037..2180110b 100644 --- a/src/main/resources/assets/do-a-barrel-roll/lang/en_us.json +++ b/src/main/resources/assets/do-a-barrel-roll/lang/en_us.json @@ -2,6 +2,10 @@ "text.autoconfig.do-a-barrel-roll.title": "§lDo a Barrel Roll", "text.autoconfig.do-a-barrel-roll.option.switchRollAndYaw": "Switch roll and yaw", "text.autoconfig.do-a-barrel-roll.option.switchRollAndYaw.@Tooltip": "Switch the roll and yaw axis, not recommended but might make flying easier for some people.", + "text.autoconfig.do-a-barrel-roll.option.momentumBasedMouse": "Use momentum based camera", + "text.autoconfig.do-a-barrel-roll.option.momentumBasedMouse.@Tooltip": "Makes it easier to make continuous movements with the mouse.", + "text.autoconfig.do-a-barrel-roll.option.showMomentumWidget": "Show momentum widget", + "text.autoconfig.do-a-barrel-roll.option.showMomentumWidget.@Tooltip": "Turn the crosshair into a helpful widget when using the momentum based camera.", "text.autoconfig.do-a-barrel-roll.option.desktopSensitivity": "Mouse and keyboard sensitivity", "text.autoconfig.do-a-barrel-roll.option.desktopSensitivity.pitch": "Pitch", "text.autoconfig.do-a-barrel-roll.option.desktopSensitivity.yaw": "Yaw", diff --git a/src/main/resources/do-a-barrel-roll.mixins.json b/src/main/resources/do-a-barrel-roll.mixins.json index ef55e910..b2020dcf 100644 --- a/src/main/resources/do-a-barrel-roll.mixins.json +++ b/src/main/resources/do-a-barrel-roll.mixins.json @@ -6,9 +6,12 @@ "client": [ "ClientPlayerEntityMixin", "GameRendererMixin", - "MouseMixin" + "MouseMixin", + "InGameHudMixin" ], "injectors": { "defaultRequire": 1 - } + }, + "mixins": [ + ] }