diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e8a115d..93f2139a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ Please use [Discord](https://discord.gg/5kcBCvnbTp) or [GitHub](https://github.c **A wiki is now available: https://github.com/Octol1ttle/FlightAssistant/wiki** +## 3.1.0 + +- Added support for Minecraft 26.1 +- Added a keybind to toggle Auto Thrust on its own, independent of the rest of the autopilot +- Added a keybind to toggle Flight Directors, Auto Thrust, and Autopilot together with one press +- Added a holding pattern autopilot mode: enter it with a keybind to hold at the waypoint you're currently flying to (or at your present position if there isn't one), or configure a fix and inbound course manually in the Autoflight setup screen +- Fixed the attitude display's pitch-limit indicators sometimes appearing far more than intended, especially at low speed + ## Changes from Beta 1 - Added Chinese localization diff --git a/build.gradle.kts b/build.gradle.kts index 24827689..16504318 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,8 @@ plugins { kotlin("jvm") kotlin("plugin.serialization") - id("dev.isxander.modstitch.base") version "0.7.0-unstable" - id("fabric-loom") version "1.14.6" apply false + id("dev.isxander.modstitch.base") version "0.8.5" + id("fabric-loom") version "1.17.20" apply false id("me.modmuss50.mod-publish-plugin") id("me.fallenbreath.yamlang") version "1.5.0" } @@ -47,15 +47,17 @@ tasks.withType { modstitch { minecraftVersion = minecraft + val j25: Boolean = stonecutter.eval(minecraft, ">=26.1") val j21: Boolean = stonecutter.eval(minecraft, ">=1.20.6") - javaVersion = if (j21) 21 else 17 + val javaVersionNumber: Int = if (j25) 25 else if (j21) 21 else 17 + javaVersion = javaVersionNumber java { withSourcesJar() } kotlin { - jvmToolchain(if (j21) 21 else 17) + jvmToolchain(javaVersionNumber) } // If parchment doesnt exist for a version yet you can safely diff --git a/gradle.properties b/gradle.properties index cd73117b..8cfc89c4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,11 +8,11 @@ org.gradle.configuration-cache=false mod.id=flightassistant mod.name=FlightAssistant mod.group=ru.octol1ttle -mod.version=3.0.0 +mod.version=3.1.0 # Global Dependencies deps.mixin_extras=0.4.1 -deps.fabric_loader=0.16.10 +deps.fabric_loader=0.19.5 deps.flk=1.13.0 publish.modrinth=CWqLEOPt diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cd4b7aa8..cf71939e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index ec47d378..64432e97 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -38,6 +38,8 @@ stonecutter { mc("1.21.6", "fabric") mc("1.21.9", "fabric") mc("1.21.11", "fabric") + mc("26.1.2", "fabric") + mc("26.2", "fabric") } } diff --git a/src/main/java/ru/octol1ttle/flightassistant/mixin/gui/GuiMixinNew.java b/src/main/java/ru/octol1ttle/flightassistant/mixin/gui/GuiMixinNew.java index a4b13173..4cdd78c7 100644 --- a/src/main/java/ru/octol1ttle/flightassistant/mixin/gui/GuiMixinNew.java +++ b/src/main/java/ru/octol1ttle/flightassistant/mixin/gui/GuiMixinNew.java @@ -5,7 +5,13 @@ @Mixin(Gui.class) abstract class GuiMixinNew { -//? if fabric && >=1.21.6 { +//? if fabric && >=26.1 { + /*@org.spongepowered.asm.mixin.injection.Inject(method = "extractRenderState", at = @org.spongepowered.asm.mixin.injection.At(value = "INVOKE", target = "Lnet/minecraft/client/gui/Gui;extractHotbarAndDecorations(Lnet/minecraft/client/gui/GuiGraphicsExtractor;Lnet/minecraft/client/DeltaTracker;)V")) + private void beforeRenderHotbarAndDecorations(net.minecraft.client.gui.GuiGraphicsExtractor guiGraphics, net.minecraft.client.DeltaTracker deltaTracker, org.spongepowered.asm.mixin.injection.callback.CallbackInfo ci) { + ru.octol1ttle.flightassistant.api.util.event.FixedGuiRenderCallback.EVENT.invoker().onRenderGui(guiGraphics, deltaTracker.getGameTimeDeltaPartialTick(true)); + guiGraphics.nextStratum(); + } +*///?} else if fabric && >=1.21.6 { /*@org.spongepowered.asm.mixin.injection.Inject(method = "render", at = @org.spongepowered.asm.mixin.injection.At(value = "INVOKE", target = "Lnet/minecraft/client/gui/Gui;renderHotbarAndDecorations(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/client/DeltaTracker;)V")) private void beforeRenderHotbarAndDecorations(net.minecraft.client.gui.GuiGraphics guiGraphics, net.minecraft.client.DeltaTracker deltaTracker, org.spongepowered.asm.mixin.injection.callback.CallbackInfo ci) { ru.octol1ttle.flightassistant.api.util.event.FixedGuiRenderCallback.EVENT.invoker().onRenderGui(guiGraphics, deltaTracker.getGameTimeDeltaPartialTick(true)); diff --git a/src/main/java/ru/octol1ttle/flightassistant/mixin/level_renderer/LevelRendererMixinNewestest.java b/src/main/java/ru/octol1ttle/flightassistant/mixin/level_renderer/LevelRendererMixinNewestest.java index b9ddd0f7..fb31055b 100644 --- a/src/main/java/ru/octol1ttle/flightassistant/mixin/level_renderer/LevelRendererMixinNewestest.java +++ b/src/main/java/ru/octol1ttle/flightassistant/mixin/level_renderer/LevelRendererMixinNewestest.java @@ -5,10 +5,15 @@ @Mixin(LevelRenderer.class) abstract class LevelRendererMixinNewestest { -//? if >=1.21.9 { +//? if >=26.1 { + /*@org.spongepowered.asm.mixin.injection.Inject(method = "renderLevel", at = @org.spongepowered.asm.mixin.injection.At("HEAD")) + private void onStartRender(com.mojang.blaze3d.resource.GraphicsResourceAllocator graphicsResourceAllocator, net.minecraft.client.DeltaTracker deltaTracker, boolean renderBlockOutline, net.minecraft.client.renderer.state.level.CameraRenderState cameraRenderState, org.joml.Matrix4fc frustumMatrix, com.mojang.blaze3d.buffers.GpuBufferSlice fogBuffer, org.joml.Vector4f fogColor, boolean renderSky, net.minecraft.client.renderer.chunk.ChunkSectionsToRender chunkSectionsToRender, org.spongepowered.asm.mixin.injection.callback.CallbackInfo ci) { + ru.octol1ttle.flightassistant.api.util.event.LevelRenderCallback.EVENT.invoker().onStartRenderLevel(deltaTracker.getGameTimeDeltaPartialTick(true), cameraRenderState.xRot, cameraRenderState.yRot, cameraRenderState.projectionMatrix, frustumMatrix.get3x3(new org.joml.Matrix3f())); + } +*///?} else if >=1.21.9 { /*@org.spongepowered.asm.mixin.injection.Inject(method = "renderLevel", at = @org.spongepowered.asm.mixin.injection.At("HEAD")) private void onStartRender(com.mojang.blaze3d.resource.GraphicsResourceAllocator graphicsResourceAllocator, net.minecraft.client.DeltaTracker deltaTracker, boolean renderBlockOutline, net.minecraft.client.Camera camera, org.joml.Matrix4f frustumMatrix, org.joml.Matrix4f projectionMatrix, org.joml.Matrix4f cullingProjectionMatrix, com.mojang.blaze3d.buffers.GpuBufferSlice fogBuffer, org.joml.Vector4f fogColor, boolean renderSky, org.spongepowered.asm.mixin.injection.callback.CallbackInfo ci) { - ru.octol1ttle.flightassistant.api.util.event.LevelRenderCallback.EVENT.invoker().onStartRenderLevel(deltaTracker.getGameTimeDeltaPartialTick(true), camera, projectionMatrix, frustumMatrix.get3x3(new org.joml.Matrix3f())); + ru.octol1ttle.flightassistant.api.util.event.LevelRenderCallback.EVENT.invoker().onStartRenderLevel(deltaTracker.getGameTimeDeltaPartialTick(true), camera.xRot, camera.yRot, projectionMatrix, frustumMatrix.get3x3(new org.joml.Matrix3f())); } *///?} } diff --git a/src/main/kotlin/net/minecraft/client/gui/GuiGraphics.kt b/src/main/kotlin/net/minecraft/client/gui/GuiGraphics.kt new file mode 100644 index 00000000..904d3adf --- /dev/null +++ b/src/main/kotlin/net/minecraft/client/gui/GuiGraphics.kt @@ -0,0 +1,4 @@ +package net.minecraft.client.gui + +//? if >=26.1 +/*typealias GuiGraphics = GuiGraphicsExtractor*/ diff --git a/src/main/kotlin/net/minecraft/client/gui/components/Renderable.kt b/src/main/kotlin/net/minecraft/client/gui/components/Renderable.kt new file mode 100644 index 00000000..e6b0fc47 --- /dev/null +++ b/src/main/kotlin/net/minecraft/client/gui/components/Renderable.kt @@ -0,0 +1,7 @@ +package net.minecraft.client.gui.components + +//? if >=26.1 { +/*fun Renderable.render(guiGraphics: net.minecraft.client.gui.GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) { + this.extractRenderState(guiGraphics, mouseX, mouseY, partialTick) +} +*///?} diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/FAKeyMappings.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/FAKeyMappings.kt index 562ac824..634b3b8d 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/FAKeyMappings.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/FAKeyMappings.kt @@ -1,11 +1,15 @@ package ru.octol1ttle.flightassistant import com.mojang.blaze3d.platform.InputConstants +import kotlin.math.roundToInt import net.minecraft.client.KeyMapping import org.lwjgl.glfw.GLFW import ru.octol1ttle.flightassistant.FlightAssistant.mc import ru.octol1ttle.flightassistant.api.computer.ComputerBus +import ru.octol1ttle.flightassistant.api.util.pointsToDirection import ru.octol1ttle.flightassistant.config.FAConfig +import ru.octol1ttle.flightassistant.impl.computer.autoflight.modes.HoldingPatternLateralMode +import ru.octol1ttle.flightassistant.impl.computer.autoflight.modes.SelectedAltitudeVerticalMode import ru.octol1ttle.flightassistant.screen.FlightAssistantSetupScreen object FAKeyMappings { @@ -24,6 +28,9 @@ object FAKeyMappings { lateinit var openFlightAssistantSetup: KeyMapping lateinit var autopilotDisconnect: KeyMapping + lateinit var toggleAutoThrust: KeyMapping + lateinit var toggleAllAutopilot: KeyMapping + lateinit var toggleHoldingPattern: KeyMapping lateinit var globalAutomationOverride: KeyMapping lateinit var hideCurrentAlert: KeyMapping @@ -38,7 +45,10 @@ object FAKeyMappings { toggleEnabled = addKeyMapping("toggle_enabled", -1) openFlightAssistantSetup = addKeyMapping("open_flightassistant_setup", GLFW.GLFW_KEY_KP_ENTER) - autopilotDisconnect = addKeyMapping("autopilot_disconnect", GLFW.GLFW_KEY_CAPS_LOCK) // TODO: replace with "Toggle FD", "Toggle A/T", "Toggle AP" + autopilotDisconnect = addKeyMapping("autopilot_disconnect", GLFW.GLFW_KEY_CAPS_LOCK) + toggleAutoThrust = addKeyMapping("toggle_auto_thrust", -1) + toggleAllAutopilot = addKeyMapping("toggle_all_autopilot", -1) + toggleHoldingPattern = addKeyMapping("toggle_holding_pattern", -1) globalAutomationOverride = addKeyMapping("global_automation_override", GLFW.GLFW_KEY_LEFT_ALT) hideCurrentAlert = addKeyMapping("hide_current_alert", GLFW.GLFW_KEY_KP_0) @@ -78,6 +88,35 @@ object FAKeyMappings { } computers.autoflight.setAutoPilot(false, alert = false) } + while (toggleAutoThrust.consumeClick()) { + computers.autoflight.setAutoThrust(!computers.autoflight.autoThrust, alert = false) + } + while (toggleAllAutopilot.consumeClick()) { + val enable: Boolean = !computers.autoflight.autopilot + computers.autoflight.setFlightDirectors(enable) + computers.autoflight.setAutoThrust(enable, alert = false) + computers.autoflight.setAutoPilot(enable, alert = false) + } + while (toggleHoldingPattern.consumeClick()) { + if (computers.autoflight.selectedLateralMode is HoldingPatternLateralMode) { + computers.autoflight.selectedLateralMode = null + computers.autoflight.selectedVerticalMode = null + } else { + // Hold at the waypoint currently being flown to, if there is one; otherwise hold at present position. + val target = computers.plan.getEnrouteTarget() + val fixX: Int = target?.coordinatesX ?: computers.data.x.roundToInt() + val fixZ: Int = target?.coordinatesZ ?: computers.data.z.roundToInt() + val fixAltitude: Int = target?.altitude ?: computers.data.altitude.roundToInt() + val inboundCourse: Float = if (target != null) { + pointsToDirection(fixX.toDouble(), fixZ.toDouble(), computers.data.x, computers.data.z).toFloat() + 180.0f + } else { + computers.data.heading + } + + computers.autoflight.selectedLateralMode = HoldingPatternLateralMode(fixX, fixZ, inboundCourse) + computers.autoflight.selectedVerticalMode = SelectedAltitudeVerticalMode(fixAltitude) + } + } while (hideCurrentAlert.consumeClick()) { computers.alert.hideCurrentAlert() diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/FlightAssistant.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/FlightAssistant.kt index d9cc7444..b3825fc5 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/FlightAssistant.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/FlightAssistant.kt @@ -43,18 +43,21 @@ object FlightAssistant { ComputerHost.sendRegistrationEvent() initComplete = true } - LevelRenderCallback.EVENT.register { partialTick, camera, projectionMatrix, frustumMatrix -> + LevelRenderCallback.EVENT.register { partialTick, cameraXRot, cameraYRot, projectionMatrix, frustumMatrix -> FAKeyMappings.checkPressed(ComputerHost) ComputerHost.tick(partialTick) RenderMatrices.projectionMatrix.set(projectionMatrix) RenderMatrices.worldSpaceMatrix.set(frustumMatrix) +//? if >=26.1 { + /*RenderMatrices.modelViewMatrix.identity() +*///?} else RenderMatrices.modelViewMatrix.set(RenderSystem.getModelViewMatrix()) RenderMatrices.worldSpaceNoRollMatrix.set(Matrix4f().apply { - rotate(Axis.XP.rotationDegrees(camera.xRot)) - rotate(Axis.YP.rotationDegrees(camera.yRot + 180.0f)) + rotate(Axis.XP.rotationDegrees(cameraXRot)) + rotate(Axis.YP.rotationDegrees(cameraYRot + 180.0f)) }) RenderMatrices.ready = true diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/ScreenSpace.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/ScreenSpace.kt index cc2992d8..36089266 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/ScreenSpace.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/ScreenSpace.kt @@ -5,14 +5,18 @@ import org.jetbrains.annotations.Contract import org.joml.Matrix4f import org.joml.Vector3f import org.joml.Vector4f -import org.lwjgl.opengl.GL11 import ru.octol1ttle.flightassistant.FlightAssistant.mc object ScreenSpace { private var viewport: IntArray = IntArray(4) internal fun updateViewport() { - GL11.glGetIntegerv(GL11.GL_VIEWPORT, viewport) + // The HUD is now extracted during the CPU render-state phase (26.x), where the + // GL viewport is stale. Project against the actual framebuffer size instead. + viewport[0] = 0 + viewport[1] = 0 + viewport[2] = mc.window.width + viewport[3] = mc.window.height } /** @@ -52,10 +56,24 @@ object ScreenSpace { val matrixModel = Matrix4f(RenderMatrices.modelViewMatrix) matrixProj.mul(matrixModel) - .project( - transformedCoordinates.x(), transformedCoordinates.y(), transformedCoordinates.z(), viewport, - target - ) + + // On 26.x the projection matrix uses a reversed-Z depth range. Points that are at + // or behind the camera plane (clip.w <= 0) get mirrored back into the visible depth + // window, so the generic z-range check below can no longer reject them. Reject them + // here explicitly, otherwise e.g. both pitch-limit arrows end up drawn at once. + val clipW: Float = + matrixProj.m03() * transformedCoordinates.x + + matrixProj.m13() * transformedCoordinates.y + + matrixProj.m23() * transformedCoordinates.z + + matrixProj.m33() * transformedCoordinates.w + if (clipW <= 0.0f) { + return Vector3f(-1.0f, -1.0f, 2.0f) + } + + matrixProj.project( + transformedCoordinates.x(), transformedCoordinates.y(), transformedCoordinates.z(), viewport, + target + ) return Vector3f( target.x / mc.window.guiScale.toFloat(), diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/event/LevelRenderCallback.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/event/LevelRenderCallback.kt index 897ca50f..ac54dab4 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/event/LevelRenderCallback.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/event/LevelRenderCallback.kt @@ -2,15 +2,14 @@ package ru.octol1ttle.flightassistant.api.util.event import dev.architectury.event.Event import dev.architectury.event.EventFactory -import net.minecraft.client.Camera import org.joml.Matrix3f -import org.joml.Matrix4f +import org.joml.Matrix4fc fun interface LevelRenderCallback { /** * Called when the LevelRenderer starts rendering the level */ - fun onStartRenderLevel(partialTick: Float, camera: Camera, projectionMatrix: Matrix4f, frustumMatrix: Matrix3f) + fun onStartRenderLevel(partialTick: Float, cameraXRot: Float, cameraYRot: Float, projectionMatrix: Matrix4fc, frustumMatrix: Matrix3f) companion object { @JvmField diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/extensions/GuiGraphicsExtensions.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/extensions/GuiGraphicsExtensions.kt index 0caf061f..54d3dd72 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/extensions/GuiGraphicsExtensions.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/api/util/extensions/GuiGraphicsExtensions.kt @@ -13,6 +13,36 @@ import ru.octol1ttle.flightassistant.config.FAConfig internal val font: Font = mc.font +//? if >=26.1 { +/*fun GuiGraphics.hLine(x1: Int, x2: Int, y: Int, color: Int) { + val minX: Int = minOf(x1, x2) + val maxX: Int = maxOf(x1, x2) + fill(minX, y, maxX + 1, y + 1, color) +} + +fun GuiGraphics.vLine(x: Int, y1: Int, y2: Int, color: Int) { + val minY: Int = minOf(y1, y2) + val maxY: Int = maxOf(y1, y2) + fill(x, minY, x + 1, maxY + 1, color) +} + +fun GuiGraphics.drawString(font: Font, text: String, x: Int, y: Int, color: Int) { + this.text(font, text, x, y, color) +} + +fun GuiGraphics.drawString(font: Font, text: String, x: Int, y: Int, color: Int, shadow: Boolean) { + this.text(font, text, x, y, color, shadow) +} + +fun GuiGraphics.drawString(font: Font, text: Component, x: Int, y: Int, color: Int) { + this.text(font, text, x, y, color) +} + +fun GuiGraphics.drawString(font: Font, text: Component, x: Int, y: Int, color: Int, shadow: Boolean) { + this.text(font, text, x, y, color, shadow) +} +*///?} + val lineHeight: Int get() = font.lineHeight @@ -156,13 +186,19 @@ fun org.joml.Matrix3x2fStack.pop() { popMatrix() } -//? if >=1.21.9 { +//? if >=26.1 { +/*fun GuiGraphics.renderOutline(x: Int, y: Int, width: Int, height: Int, color: Int) { + outline(x, y, width, height, color) +} +*///?} +//? if >=1.21.11 && <26.1 { +/*fun GuiGraphics.renderOutline(x: Int, y: Int, width: Int, height: Int, color: Int) { + renderOutline(x, y, width, height, color) +} +*///?} +//? if >=1.21.9 && <1.21.11 { /*fun GuiGraphics.renderOutline(x: Int, y: Int, width: Int, height: Int, color: Int) { -//? if >=1.21.11 { - /^renderOutline( -^///?} else - submitOutline( - x, y, width, height, color) + submitOutline(x, y, width, height, color) renderDeferredElements() } *///?} \ No newline at end of file diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/autoflight/FlightPlanComputer.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/autoflight/FlightPlanComputer.kt index ed4efa4e..02b1f104 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/autoflight/FlightPlanComputer.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/autoflight/FlightPlanComputer.kt @@ -120,7 +120,7 @@ class FlightPlanComputer(computers: ComputerBus) : Computer(computers) { return enrouteData.singleOrNull { it.active == EnrouteWaypoint.Active.ORIGIN } } - private fun getEnrouteTarget(): EnrouteWaypoint? { + fun getEnrouteTarget(): EnrouteWaypoint? { return enrouteData.singleOrNull { it.active == EnrouteWaypoint.Active.TARGET } } diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/autoflight/modes/BuiltInLateralModes.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/autoflight/modes/BuiltInLateralModes.kt index 70e0e1ae..c98878f3 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/autoflight/modes/BuiltInLateralModes.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/autoflight/modes/BuiltInLateralModes.kt @@ -1,5 +1,8 @@ package ru.octol1ttle.flightassistant.impl.computer.autoflight.modes +import kotlin.math.cos +import kotlin.math.roundToInt +import kotlin.math.sin import net.minecraft.network.chat.Component import org.joml.Vector2d import ru.octol1ttle.flightassistant.api.autoflight.ControlInput @@ -44,4 +47,50 @@ data class TrackNavigationLateralMode(val originX: Int, val originZ: Int, overri val trackNormalized: Vector2d = track.normalize() return closestTrackPoint.add(trackNormalized.mul(computers.data.velocityPerSecond.horizontalDistance() * 3.0)) } +} + +class HoldingPatternLateralMode( + val fixX: Int, + val fixZ: Int, + val inboundCourse: Float, + val legLength: Double = 300.0, + val turnDirection: TurnDirection = TurnDirection.RIGHT, + override val textOverride: Component? = null +) : AutoFlightComputer.LateralMode, AutoFlightComputer.FollowsCoordinatesMode { + override val targetX: Int get() = fixX + override val targetZ: Int get() = fixZ + + // The far turn point of the racetrack: offset from the fix along the outbound leg, + // then offset sideways so alternating direct-to steering traces an oval instead of a straight line. + private val outboundPoint: Vector2d = run { + val outboundCourse: Double = inboundCourse + 180.0 + val sideCourse: Double = outboundCourse + 90.0 * turnDirection.sign + vec2dFromInts(fixX, fixZ) + .add(directionVector(outboundCourse).mul(legLength)) + .add(directionVector(sideCourse).mul(legLength / 3.0)) + } + + private var flyingOutbound: Boolean = true + + override fun getControlInput(computers: ComputerBus): ControlInput { + val fix: Vector2d = vec2dFromInts(fixX, fixZ) + val target: Vector2d = if (flyingOutbound) outboundPoint else fix + if (target.distance(computers.data.x, computers.data.z) < computers.data.velocityPerSecond.horizontalDistance() * 3.0) { + flyingOutbound = !flyingOutbound + } + + return DirectCoordinatesLateralMode(target.x.roundToInt(), target.y.roundToInt()).getControlInput(computers) + .copy(text = Component.translatable("mode.flightassistant.lateral.holding_pattern")) + } + + enum class TurnDirection(val sign: Double) { + LEFT(-1.0), RIGHT(1.0) + } + + companion object { + private fun directionVector(courseDegrees: Double): Vector2d { + val radians: Double = Math.toRadians(courseDegrees) + return Vector2d(-sin(radians), cos(radians)) + } + } } \ No newline at end of file diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/data/AirDataComputer.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/data/AirDataComputer.kt index 4c37920d..f3be75ac 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/data/AirDataComputer.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/impl/computer/data/AirDataComputer.kt @@ -70,10 +70,12 @@ class AirDataComputer(computers: ComputerBus, private val mc: Minecraft) : Compu val heading: Float get() = (yaw + 180.0f).throwIfNotInRange(0.0f..360.0f) + // Normalizing a near-zero velocity amplifies noise into a wildly unstable direction, + // so fall back to the player's actual look direction when barely moving. val flightPitch: Float - get() = degrees(asin(velocity.normalize().y).toFloat()) + get() = if (velocity.lengthSqr() < MIN_VELOCITY_LENGTH_SQR) pitch else degrees(asin(velocity.normalize().y).toFloat()) val flightYaw: Float - get() = degrees(atan2(-velocity.x, velocity.z).toFloat()) + get() = if (velocity.lengthSqr() < MIN_VELOCITY_LENGTH_SQR) yaw else degrees(atan2(-velocity.x, velocity.z).toFloat()) override fun tick() { forwardVelocity = computeForwardVector(velocity) @@ -114,5 +116,6 @@ class AirDataComputer(computers: ComputerBus, private val mc: Minecraft) : Compu companion object { val ID: ResourceLocation = FlightAssistant.id("air_data") + private const val MIN_VELOCITY_LENGTH_SQR: Double = 0.0025 // 0.05 blocks/tick } } \ No newline at end of file diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/FABaseScreen.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/FABaseScreen.kt index 824f4a87..81913bd3 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/FABaseScreen.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/FABaseScreen.kt @@ -21,6 +21,15 @@ abstract class FABaseScreen(val parent: Screen?, title: Component) : Screen(titl this.addRenderableWidget(SmartStringWidget(this.centerX, 7, this.title).middleAligned()) } +//? if >=26.1 { + /*override fun extractRenderState(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) { + this.render(guiGraphics, mouseX, mouseY, delta) + } + + open fun render(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) { + super.extractRenderState(guiGraphics, mouseX, mouseY, delta) + } +*///?} else { override fun render(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) { //? if <1.21.6 { this.renderBackground( @@ -30,6 +39,7 @@ abstract class FABaseScreen(val parent: Screen?, title: Component) : Screen(titl //?} super.render(guiGraphics, mouseX, mouseY, delta) } +//?} override fun onClose() { this.minecraft!!.setScreen(parent) diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/autoflight/AutoFlightScreen.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/autoflight/AutoFlightScreen.kt index 4f9e7cda..d997aa0f 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/autoflight/AutoFlightScreen.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/autoflight/AutoFlightScreen.kt @@ -144,6 +144,24 @@ class AutoFlightScreen(parent: Screen) : FABaseScreen(parent, Component.translat refreshableElements.add(this.addRenderableWidget(TypeStrictEditBox(xEditBox.x + xEditBox.width + 4, xEditBox.y, xEditBox.width, xEditBox.height, state.targetCoordinatesZ, { state.targetCoordinatesZ = it }, String::toIntOrNullWithFallback))) } + AutoFlightScreenState.LateralMode.HOLD -> { + val string: SmartStringWidget = this.addRenderableWidget(SmartStringWidget(baseX, baseY, Component.translatable("menu.flightassistant.autoflight.target.hold_fix"))) + refreshableElements.add(string) + val xEditBox: TypeStrictEditBox = this.addRenderableWidget(TypeStrictEditBox(string.x + string.width, string.y - 2, baseWidth + 4, baseHeight, state.holdFixX, { state.holdFixX = it }, String::toIntOrNullWithFallback)) + refreshableElements.add(xEditBox) + refreshableElements.add(this.addRenderableWidget(TypeStrictEditBox(xEditBox.x + xEditBox.width + 4, xEditBox.y, xEditBox.width, xEditBox.height, state.holdFixZ, { state.holdFixZ = it }, String::toIntOrNullWithFallback))) + + val courseString: SmartStringWidget = this.addRenderableWidget(SmartStringWidget(baseX, baseY + 16, Component.translatable("menu.flightassistant.autoflight.target.hold_course"))) + refreshableElements.add(courseString) + refreshableElements.add( + this.addRenderableWidget( + TypeStrictEditBox( + courseString.x + courseString.width, courseString.y - 2, baseWidth, baseHeight, state.holdInboundCourse, { state.holdInboundCourse = it }, + String::toIntOrNullWithFallback, { it in 0..360 }) + ) + ) + } + AutoFlightScreenState.LateralMode.FLIGHT_PLAN -> Unit } } diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/autoflight/AutoFlightScreenState.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/autoflight/AutoFlightScreenState.kt index b1d7641f..942542e5 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/autoflight/AutoFlightScreenState.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/autoflight/AutoFlightScreenState.kt @@ -5,6 +5,7 @@ import net.minecraft.network.chat.Component import ru.octol1ttle.flightassistant.impl.computer.autoflight.AutoFlightComputer import ru.octol1ttle.flightassistant.impl.computer.autoflight.modes.DirectCoordinatesLateralMode import ru.octol1ttle.flightassistant.impl.computer.autoflight.modes.HeadingLateralMode +import ru.octol1ttle.flightassistant.impl.computer.autoflight.modes.HoldingPatternLateralMode import ru.octol1ttle.flightassistant.impl.computer.autoflight.modes.PitchVerticalMode import ru.octol1ttle.flightassistant.impl.computer.autoflight.modes.SelectedAltitudeVerticalMode import ru.octol1ttle.flightassistant.impl.computer.autoflight.modes.SpeedThrustMode @@ -24,6 +25,9 @@ class AutoFlightScreenState { var targetHeading: Int = 360 var targetCoordinatesX: Int = 0 var targetCoordinatesZ: Int = 0 + var holdFixX: Int = 0 + var holdFixZ: Int = 0 + var holdInboundCourse: Int = 360 fun apply(autoFlight: AutoFlightComputer) { autoFlight.selectedThrustMode = when (thrustMode) { @@ -39,6 +43,7 @@ class AutoFlightScreenState { autoFlight.selectedLateralMode = when (lateralMode) { LateralMode.HEADING -> HeadingLateralMode(targetHeading) LateralMode.COORDINATES -> DirectCoordinatesLateralMode(targetCoordinatesX, targetCoordinatesZ) + LateralMode.HOLD -> HoldingPatternLateralMode(holdFixX, holdFixZ, holdInboundCourse.toFloat()) LateralMode.FLIGHT_PLAN -> null } } @@ -66,6 +71,7 @@ class AutoFlightScreenState { enum class LateralMode(@JvmField val displayName: Component) : NameableEnum { HEADING(Component.translatable("menu.flightassistant.autoflight.lateral.heading")), COORDINATES(Component.translatable("menu.flightassistant.autoflight.lateral.coordinates")), + HOLD(Component.translatable("menu.flightassistant.autoflight.lateral.hold")), FLIGHT_PLAN(Component.translatable("menu.flightassistant.autoflight.lateral.flight_plan")); override fun getDisplayName(): Component { diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/CycleTextOnlyButton.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/CycleTextOnlyButton.kt index 76bc38c3..40c325e5 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/CycleTextOnlyButton.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/CycleTextOnlyButton.kt @@ -11,6 +11,7 @@ import net.minecraft.network.chat.MutableComponent import net.minecraft.network.chat.Style import net.minecraft.util.Mth import ru.octol1ttle.flightassistant.api.util.extensions.appendWithSeparation +import ru.octol1ttle.flightassistant.api.util.extensions.drawString import ru.octol1ttle.flightassistant.api.util.extensions.font import ru.octol1ttle.flightassistant.api.util.extensions.whiteColor @@ -24,7 +25,9 @@ class CycleTextOnlyButton(x: Int, y: Int, private val entries: refreshMessage() } -//? if >=1.21.11 { +//? if >=26.1 { + /*override fun extractContents( +*///?} else if >=1.21.11 { /*override fun renderContents( *///?} else override fun renderWidget( diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/TextOnlyButton.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/TextOnlyButton.kt index fd4da20f..e9cc0d48 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/TextOnlyButton.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/TextOnlyButton.kt @@ -7,12 +7,15 @@ import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentUtils import net.minecraft.network.chat.Style import net.minecraft.util.Mth +import ru.octol1ttle.flightassistant.api.util.extensions.drawString import ru.octol1ttle.flightassistant.api.util.extensions.font class TextOnlyButton(val baseX: Int, y: Int, text: Component, onPress: OnPress) : Button(baseX - font.width(text) / 2, y, font.width(text), font.lineHeight, text, onPress, DEFAULT_NARRATION) { var color: Int = 0 -//? if >=1.21.11 { +//? if >=26.1 { + /*override fun extractContents( +*///?} else if >=1.21.11 { /*override fun renderContents( *///?} else override fun renderWidget( diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/TypeStrictEditBox.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/TypeStrictEditBox.kt index 71c6f031..9211e60c 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/TypeStrictEditBox.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/components/TypeStrictEditBox.kt @@ -7,13 +7,32 @@ import net.minecraft.client.gui.components.EditBox import net.minecraft.network.chat.Component import ru.octol1ttle.flightassistant.api.util.extensions.font -class TypeStrictEditBox(x: Int, y: Int, width: Int, height: Int, initialValue: T, onValueChange: Consumer, convertFunction: (String) -> T?, filter: Predicate = Predicates.alwaysTrue()) : EditBox(font, x, y, width, height, Component.empty()) { +class TypeStrictEditBox(x: Int, y: Int, width: Int, height: Int, initialValue: T, onValueChange: Consumer, private val convertFunction: (String) -> T?, private val filter: Predicate = Predicates.alwaysTrue()) : EditBox(font, x, y, width, height, Component.empty()) { init { this.value = initialValue.toString() +//? if <26.1 { this.setFilter { val value: T? = convertFunction.invoke(it) value != null && filter.test(value) } +//?} this.setResponder { onValueChange.accept(convertFunction.invoke(it)!!) } } + +//? if >=26.1 { + /*private fun isValid(candidate: String): Boolean { + val value: T? = convertFunction.invoke(candidate) + return value != null && filter.test(value) + } + + override fun insertText(text: String) { + val previousValue: String = this.value + val previousCursor: Int = this.cursorPosition + super.insertText(text) + if (!isValid(this.value)) { + this.value = previousValue + this.setCursorPosition(previousCursor) + } + } +*///?} } \ No newline at end of file diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/fms/enroute/EnrouteWaypointsList.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/fms/enroute/EnrouteWaypointsList.kt index b179c211..2d239ba9 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/fms/enroute/EnrouteWaypointsList.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/fms/enroute/EnrouteWaypointsList.kt @@ -8,6 +8,9 @@ import net.minecraft.client.gui.components.Tooltip import net.minecraft.client.gui.components.events.GuiEventListener import net.minecraft.client.gui.narration.NarratableEntry import net.minecraft.network.chat.Component +//? if >=26.1 { +/*import net.minecraft.client.gui.components.render +*///?} import ru.octol1ttle.flightassistant.api.computer.ComputerBus import ru.octol1ttle.flightassistant.api.util.extensions.* import ru.octol1ttle.flightassistant.impl.computer.autoflight.FlightPlanComputer @@ -52,7 +55,11 @@ class EnrouteWaypointsList(y0: Int, y1: Int, width: Int, val columns: Float, val private var lastFlightPlanActive: FlightPlanComputer.EnrouteWaypoint.Active? = state.active -//? if >=1.21.9 { +//? if >=26.1 { + /*override fun extractContent(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, hovering: Boolean, partialTick: Float) { + val top = contentY + val index = list.children().indexOf(this) +*///?} else if >=1.21.9 { /*override fun renderContent(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, hovering: Boolean, partialTick: Float) { val top = contentY val index = list.children().indexOf(this) diff --git a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/system/SystemManagementList.kt b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/system/SystemManagementList.kt index b6573228..13568d33 100644 --- a/src/main/kotlin/ru/octol1ttle/flightassistant/screen/system/SystemManagementList.kt +++ b/src/main/kotlin/ru/octol1ttle/flightassistant/screen/system/SystemManagementList.kt @@ -8,6 +8,9 @@ import net.minecraft.client.gui.components.events.GuiEventListener import net.minecraft.client.gui.narration.NarratableEntry import net.minecraft.network.chat.Component import net.minecraft.resources.ResourceLocation +//? if >=26.1 { +/*import net.minecraft.client.gui.components.render +*///?} import ru.octol1ttle.flightassistant.api.ModuleController import ru.octol1ttle.flightassistant.api.util.extensions.cautionColor import ru.octol1ttle.flightassistant.api.util.extensions.font @@ -31,7 +34,10 @@ class SystemManagementList(y0: Int, y1: Int, width: Int, baseKey: String, contro val children = listOf(this.displayName, faultText, offText, toggleButton) -//? if >=1.21.9 { +//? if >=26.1 { + /*override fun extractContent(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, hovering: Boolean, partialTick: Float) { + val top = contentY +*///?} else if >=1.21.9 { /*override fun renderContent(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, hovering: Boolean, partialTick: Float) { val top = contentY *///?} else { diff --git a/src/main/resources/assets/flightassistant/lang/en_us.yml b/src/main/resources/assets/flightassistant/lang/en_us.yml index 3e241724..ec3a67ed 100644 --- a/src/main/resources/assets/flightassistant/lang/en_us.yml +++ b/src/main/resources/assets/flightassistant/lang/en_us.yml @@ -291,6 +291,7 @@ menu.flightassistant: mode: 'Lateral mode: ' heading: HDG coordinates: COORDS + hold: HOLD flight_plan: F-PLAN target: speed: 'Target speed: ' @@ -300,6 +301,8 @@ menu.flightassistant: altitude: 'Target altitude: ' heading: 'Target heading: ' coordinates: 'Target coordinates: ' + hold_fix: 'Hold fix coordinates: ' + hold_course: 'Hold inbound course: ' fms: .: Flight planning departure: @@ -372,6 +375,7 @@ mode.flightassistant: heading: HDG direct_coordinates: DIR COORDS track_navigation: TRK NAV + holding_pattern: HOLD localizer: LOC go_around: GA HDG @@ -428,6 +432,9 @@ key.category.flightassistant: toggle_enabled: Toggle mod enabled open_flightassistant_setup: Open FlightAssistant setup screen autopilot_disconnect: Disconnect autopilot + toggle_auto_thrust: Toggle Auto Thrust + toggle_all_autopilot: Toggle all autopilot functionality + toggle_holding_pattern: Enter/exit holding pattern global_automation_override: Global automation override (hold) hide_current_alert: Hide currently active alert show_hidden_alert: Show last hidden alert diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index b2d43790..b6a04fc7 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -1,6 +1,6 @@ plugins { - kotlin("jvm") version "2.2.0" apply false - kotlin("plugin.serialization") version "2.2.0" apply false + kotlin("jvm") version "2.4.20" apply false + kotlin("plugin.serialization") version "2.4.20" apply false id("dev.kikugie.stonecutter") id("me.modmuss50.mod-publish-plugin") version "0.8.4" apply false } diff --git a/versions/1.20.1-fabric/gradle.properties b/versions/1.20.1-fabric/gradle.properties index a22bd0ac..ae8405f5 100644 --- a/versions/1.20.1-fabric/gradle.properties +++ b/versions/1.20.1-fabric/gradle.properties @@ -1,4 +1,4 @@ -modstitch.platform=loom +modstitch.platform=fabric-loom-remap # Global deps.parchment=2023.09.03 diff --git a/versions/1.21.1-fabric/gradle.properties b/versions/1.21.1-fabric/gradle.properties index 820147d5..3168084b 100644 --- a/versions/1.21.1-fabric/gradle.properties +++ b/versions/1.21.1-fabric/gradle.properties @@ -1,4 +1,4 @@ -modstitch.platform=loom +modstitch.platform=fabric-loom-remap # Global deps.parchment=2024.11.17 diff --git a/versions/1.21.11-fabric/gradle.properties b/versions/1.21.11-fabric/gradle.properties index 6a41f119..1af32a11 100644 --- a/versions/1.21.11-fabric/gradle.properties +++ b/versions/1.21.11-fabric/gradle.properties @@ -1,4 +1,4 @@ -modstitch.platform=loom +modstitch.platform=fabric-loom-remap # Global deps.parchment=2025.12.20 diff --git a/versions/1.21.6-fabric/gradle.properties b/versions/1.21.6-fabric/gradle.properties index ddff0e39..d5d20c99 100644 --- a/versions/1.21.6-fabric/gradle.properties +++ b/versions/1.21.6-fabric/gradle.properties @@ -1,4 +1,4 @@ -modstitch.platform=loom +modstitch.platform=fabric-loom-remap # Global deps.parchment=2025.06.29 diff --git a/versions/1.21.9-fabric/gradle.properties b/versions/1.21.9-fabric/gradle.properties index 15f9bf23..fde5e70a 100644 --- a/versions/1.21.9-fabric/gradle.properties +++ b/versions/1.21.9-fabric/gradle.properties @@ -1,4 +1,4 @@ -modstitch.platform=loom +modstitch.platform=fabric-loom-remap # Global deps.parchment=2025.10.05 diff --git a/versions/26.1.2-fabric/gradle.properties b/versions/26.1.2-fabric/gradle.properties new file mode 100644 index 00000000..543e8e4f --- /dev/null +++ b/versions/26.1.2-fabric/gradle.properties @@ -0,0 +1,16 @@ +modstitch.platform=fabric-loom + +# Global +# No deps.parchment: no Parchment mappings published for 26.1 yet, falls back to official mappings. +deps.yacl=3.9.6+26.1-fabric +deps.arch_api=20.1.14 + +# Fabric +deps.fapi=0.155.3+26.1.2 +deps.modmenu=18.0.1 +deps.dabr=3.8.4+26.1-fabric + +# Mod +mod.mc_version_range=>=26.1 <26.2 +mod.mc_title=26.1.2 +mod.mc_targets=26.1 26.1.1 26.1.2 diff --git a/versions/26.2-fabric/gradle.properties b/versions/26.2-fabric/gradle.properties new file mode 100644 index 00000000..77410d8c --- /dev/null +++ b/versions/26.2-fabric/gradle.properties @@ -0,0 +1,16 @@ +modstitch.platform=fabric-loom + +# Global +# No deps.parchment: no Parchment mappings published for 26.2 yet, falls back to official mappings. +deps.yacl=3.9.6+26.2-fabric +deps.arch_api=21.1.9 + +# Fabric +deps.fapi=0.160.0+26.2 +deps.modmenu=20.0.2 +deps.dabr=3.8.4+26.2-fabric + +# Mod +mod.mc_version_range=26.2 +mod.mc_title=26.2 +mod.mc_targets=26.2