diff --git a/src/main/java/net/moonleay/gimbal/client/ClientMain.kt b/src/main/java/net/moonleay/gimbal/client/ClientMain.kt
index 0f6f156..2af8b59 100644
--- a/src/main/java/net/moonleay/gimbal/client/ClientMain.kt
+++ b/src/main/java/net/moonleay/gimbal/client/ClientMain.kt
@@ -21,7 +21,9 @@ package net.moonleay.gimbal.client
import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents
+import net.fabricmc.loader.api.FabricLoader
import net.moonleay.gimbal.build.BuildConstants
+import net.moonleay.gimbal.client.config.ClientConfigHolder
import net.moonleay.gimbal.client.editor.ClientEditor
import net.moonleay.gimbal.client.keybindings.KeybindingManager
import net.moonleay.gimbal.client.keybindings.KeybindingRegistrar
@@ -30,6 +32,7 @@ import org.apache.logging.log4j.LogManager
internal object ClientMain : ClientModInitializer {
private val LOGGER = LogManager.getLogger(BuildConstants.modName)
+ lateinit var CONFIG: ClientConfigHolder
override fun onInitializeClient() {
@@ -39,6 +42,10 @@ internal object ClientMain : ClientModInitializer {
LOGGER.info("Registering packets...")
GimbalClient.registerPacketHandlers()
LOGGER.info("Packets have been registered.")
+ LOGGER.info("Loading client config.")
+ val gimbalConfigPath = FabricLoader.getInstance().configDir.resolve(BuildConstants.modId + "/client.json")
+ CONFIG = ClientConfigHolder(gimbalConfigPath)
+ LOGGER.info("Config has been loaded.")
LOGGER.info("Gimbal has been initialized on the client side.")
}
diff --git a/src/main/java/net/moonleay/gimbal/client/config/AnchorPoint.kt b/src/main/java/net/moonleay/gimbal/client/config/AnchorPoint.kt
new file mode 100644
index 0000000..29f34a2
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/client/config/AnchorPoint.kt
@@ -0,0 +1,30 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.client.config
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+enum class AnchorPoint {
+ TOP_LEFT,
+ TOP_RIGHT,
+ BOTTOM_RIGHT,
+ BOTTOM_LEFT,
+ CENTER
+}
diff --git a/src/main/java/net/moonleay/gimbal/client/config/Centerpoint.kt b/src/main/java/net/moonleay/gimbal/client/config/Centerpoint.kt
new file mode 100644
index 0000000..2cb8029
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/client/config/Centerpoint.kt
@@ -0,0 +1,27 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.client.config
+
+enum class Centerpoint {
+ TOP_LEFT,
+ TOP_RIGHT,
+ BOTTOM_RIGHT,
+ BOTTOM_LEFT,
+ CENTER
+}
diff --git a/src/main/java/net/moonleay/gimbal/client/config/ClientConfigHolder.kt b/src/main/java/net/moonleay/gimbal/client/config/ClientConfigHolder.kt
new file mode 100644
index 0000000..c385f49
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/client/config/ClientConfigHolder.kt
@@ -0,0 +1,70 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.client.config
+
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.decodeFromStream
+import kotlinx.serialization.json.encodeToStream
+import java.nio.file.Path
+import kotlin.io.path.*
+
+class ClientConfigHolder(private val path: Path) {
+ var config: GimbalClientConfig
+ private set
+
+ init {
+ config = load()
+ }
+
+ fun updateConfig(new: GimbalClientConfig) {
+ config = new
+ save(config)
+ }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ fun load(): GimbalClientConfig {
+ if (!path.isReadable())
+ save(GimbalClientConfig())
+ return path.inputStream().use {
+ Json.decodeFromStream(it)
+ }
+ }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ fun save(conf: GimbalClientConfig) {
+ if (!path.isWritable()) {
+ if (!path.parent.exists()) {
+ path.parent.createDirectory()
+ }
+ if (!path.parent.isWritable()) {
+ throw Exception("Parent of path ${path.parent.pathString} is not writable.")
+ }
+ if (!path.exists())
+ path.createFile()
+ if (!path.isWritable()) {
+ throw Exception("Path (${path.pathString}) exists, but is not writable.")
+ }
+ }
+ path.outputStream().use {
+ Json.encodeToStream(conf, it)
+ }
+ }
+
+}
diff --git a/src/main/java/net/moonleay/gimbal/client/config/GimbalClientConfig.kt b/src/main/java/net/moonleay/gimbal/client/config/GimbalClientConfig.kt
new file mode 100644
index 0000000..b0184d7
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/client/config/GimbalClientConfig.kt
@@ -0,0 +1,27 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.client.config
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class GimbalClientConfig(
+ val guiSettings: GimbalGuiSettings = GimbalGuiSettings(),
+ val showToasts: Boolean = true,
+)
diff --git a/src/main/java/net/moonleay/gimbal/client/config/GimbalGuiSettings.kt b/src/main/java/net/moonleay/gimbal/client/config/GimbalGuiSettings.kt
new file mode 100644
index 0000000..2a274b0
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/client/config/GimbalGuiSettings.kt
@@ -0,0 +1,28 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.client.config
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class GimbalGuiSettings(
+ val showHud: Boolean = true,
+ val scaledRes: ScaledRes = ScaledRes(4.0, 4.0),
+ val anchorPoint: AnchorPoint = AnchorPoint.TOP_LEFT,
+)
diff --git a/src/main/java/net/moonleay/gimbal/client/config/ScaledRes.kt b/src/main/java/net/moonleay/gimbal/client/config/ScaledRes.kt
new file mode 100644
index 0000000..acf9a70
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/client/config/ScaledRes.kt
@@ -0,0 +1,27 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.client.config
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class ScaledRes(
+ val scaledX: Double,
+ val scaledY: Double,
+)
diff --git a/src/main/java/net/moonleay/gimbal/client/screen/GimbalEditHudGui.kt b/src/main/java/net/moonleay/gimbal/client/screen/GimbalEditHudGui.kt
new file mode 100644
index 0000000..0b7fd61
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/client/screen/GimbalEditHudGui.kt
@@ -0,0 +1,135 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.client.screen
+
+import net.minecraft.client.gui.screen.Screen
+import net.minecraft.client.gui.widget.ButtonWidget
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.screen.ScreenTexts
+import net.minecraft.text.Text
+import net.moonleay.gimbal.build.BuildConstants
+import net.moonleay.gimbal.client.config.*
+import net.moonleay.gimbal.client.util.screen.ScreenUtil
+import org.apache.logging.log4j.LogManager
+
+class GimbalEditHudGui(private val parent: Screen, private val cfg: ClientConfigHolder) :
+ Screen(Text.translatable("gimbal.gui.edithud")) {
+ private val LOGGER = LogManager.getLogger(BuildConstants.modName)
+
+ private var tempXOffset = 4.0
+ private var tempYOffset = 4.0
+ private var shouldFollow = false
+
+ init {
+ this.tempXOffset = cfg.config.guiSettings.scaledRes.scaledX
+ this.tempYOffset = cfg.config.guiSettings.scaledRes.scaledY
+ }
+
+ override fun init() {
+ super.init()
+ this.addDrawableChild(ButtonWidget(
+ this.width / 2 + 10, this.height - 27, 90, 20, Text.translatable("gimbal.gui.resethud")
+ ) { _: ButtonWidget? ->
+ tempXOffset = 4.0
+ tempYOffset = 4.0
+ shouldFollow = false
+ })
+
+ this.addDrawableChild(ButtonWidget(
+ this.width / 2 - 100, this.height - 27, 90, 20, ScreenTexts.DONE
+ ) { _: ButtonWidget? ->
+ this.save()
+ this.client!!.setScreen(
+ this.parent
+ )
+ })
+ }
+
+ private fun save() {
+ val oldConf = cfg.config
+ if (ScreenUtil.isPositionOutOfBounds(this.tempXOffset, this.tempYOffset)) {
+ LOGGER.info("Text is not in bounds. This will not be saved in order to keep the user from making the hud inaccessible.")
+ return
+ }
+ LOGGER.info("Saving Pos...")
+
+ val newConf = GimbalClientConfig(
+ showToasts = oldConf.showToasts,
+ guiSettings = GimbalGuiSettings(
+ showHud = oldConf.guiSettings.showHud,
+ scaledRes = ScaledRes(
+ scaledX = this.tempXOffset,
+ scaledY = this.tempYOffset,
+ )
+ )
+ )
+
+ cfg.updateConfig(newConf)
+ }
+
+ override fun close() {
+ this.save()
+ this.client!!.setScreen(this.parent)
+ }
+
+ override fun tick() {
+ super.tick()
+ if (this.shouldFollow) {
+ val mouse = this.client?.mouse!!
+ val scaleFactor = this.client!!.window.calculateScaleFactor(
+ this.client!!.options.guiScale.value,
+ this.client!!.forcesUnicodeFont()
+ )
+ this.tempXOffset = ScreenUtil.getScaled(this.width, mouse.x.toFloat() / scaleFactor)
+ this.tempYOffset = ScreenUtil.getScaled(this.height, mouse.y.toFloat() / scaleFactor)
+ }
+ }
+
+ override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
+ this.shouldFollow = true
+ return super.mouseClicked(mouseX, mouseY, button)
+ }
+
+ override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
+ this.shouldFollow = false
+ return super.mouseReleased(mouseX, mouseY, button)
+ }
+
+ override fun render(matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float) {
+ this.renderBackground(matrices)
+ drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 15, 16777215)
+ this.textRenderer.drawWithShadow(
+ matrices,
+ Text.translatable("gimbal.gui.examplehud"),
+ ScreenUtil.getXForRender(
+ ScreenUtil.getReal(this.width, this.tempXOffset),
+ AnchorPoint.CENTER,
+ this.textRenderer.getWidth(Text.translatable("gimbal.gui.examplehud"))
+ ),
+ ScreenUtil.getYForRender(
+ ScreenUtil.getReal(this.height, this.tempYOffset),
+ AnchorPoint.CENTER,
+ this.textRenderer.fontHeight
+ ),
+ 0xFFFFFF
+ ) // TODO: improve placement when changing the scaling
+// LOGGER.info("width: ${this.width}, height: ${this.height}, posX: ${ScreenUtil.getReal(this.width, this.tempXOffset)}, posY: ${ScreenUtil.getReal(this.height, this.tempYOffset)}, scaleX: ${this.tempXOffset}, scaleY: ${this.tempYOffset}")
+ super.render(matrices, mouseX, mouseY, delta)
+ }
+}
diff --git a/src/main/java/net/moonleay/gimbal/client/screen/GimbalSettingsGui.kt b/src/main/java/net/moonleay/gimbal/client/screen/GimbalSettingsGui.kt
new file mode 100644
index 0000000..99cedbb
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/client/screen/GimbalSettingsGui.kt
@@ -0,0 +1,84 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.client.screen
+
+import net.minecraft.client.gui.screen.Screen
+import net.minecraft.client.gui.widget.ButtonWidget
+import net.minecraft.client.gui.widget.CyclingButtonWidget
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.screen.ScreenTexts
+import net.minecraft.text.Text
+import net.moonleay.gimbal.client.config.ClientConfigHolder
+import net.moonleay.gimbal.client.config.GimbalClientConfig
+import net.moonleay.gimbal.client.config.GimbalGuiSettings
+
+class GimbalSettingsGui(private val parent: Screen, private val cfg: ClientConfigHolder) :
+ Screen(Text.translatable("gimbal.gui.settings")) {
+ override fun init() {
+ this.addDrawableChild(CyclingButtonWidget.onOffBuilder(
+// Text.translatable("gimbal.gui.enabled"),
+// Text.translatable("gimbal.gui.disabled")
+ )
+ .initially(cfg.config.guiSettings.showHud)
+ .build(
+ this.width / 2 - 155,
+ this.height / 6 + 24 * 0,
+ 150,
+ 20,
+ Text.translatable("gimbal.gui.hud")
+ ) { _: CyclingButtonWidget?, isEnabled: Boolean? ->
+ val oldGui = cfg.config.guiSettings
+ val newGui = GimbalGuiSettings(
+ showHud = isEnabled ?: true,
+ scaledRes = oldGui.scaledRes,
+ anchorPoint = oldGui.anchorPoint
+ )
+ val newConf = GimbalClientConfig(
+ guiSettings = newGui,
+ showToasts = cfg.config.showToasts
+ )
+ cfg.updateConfig(newConf)
+ })
+ this.addDrawableChild(ButtonWidget(
+ this.width / 2 - 155 + 160,
+ this.height / 6 + 24 * 0,
+ 150,
+ 20,
+ Text.translatable("gimbal.gui.edithud")
+ ) { _: ButtonWidget? ->
+ this.client!!.setScreen(GimbalEditHudGui(this, cfg))
+ }
+ )
+
+ // Done button
+ this.addDrawableChild(ButtonWidget(
+ this.width / 2 - 100, this.height / 6 + 168, 200, 20, ScreenTexts.DONE
+ ) { _: ButtonWidget? ->
+ this.client!!.setScreen(
+ this.parent
+ )
+ })
+ }
+
+ override fun render(matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float) {
+ this.renderBackground(matrices)
+ drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 15, 16777215)
+ super.render(matrices, mouseX, mouseY, delta)
+ }
+}
diff --git a/src/main/java/net/moonleay/gimbal/client/util/screen/ScreenUtil.kt b/src/main/java/net/moonleay/gimbal/client/util/screen/ScreenUtil.kt
new file mode 100644
index 0000000..c9166cb
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/client/util/screen/ScreenUtil.kt
@@ -0,0 +1,61 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.client.util.screen
+
+import net.moonleay.gimbal.client.config.AnchorPoint
+
+object ScreenUtil {
+
+ fun getXForRender(x: Float, anchor: AnchorPoint, textWidth: Int): Float {
+ return when (anchor) {
+ AnchorPoint.TOP_LEFT -> x
+ AnchorPoint.BOTTOM_LEFT -> x
+ AnchorPoint.TOP_RIGHT -> x - textWidth
+ AnchorPoint.BOTTOM_RIGHT -> x - textWidth
+ AnchorPoint.CENTER -> x - textWidth / 2
+ }
+ }
+
+ fun getYForRender(y: Float, anchor: AnchorPoint, textHeight: Int): Float {
+ return when (anchor) {
+ AnchorPoint.TOP_LEFT -> y
+ AnchorPoint.TOP_RIGHT -> y
+ AnchorPoint.BOTTOM_RIGHT -> y - textHeight
+ AnchorPoint.BOTTOM_LEFT -> y - textHeight
+ AnchorPoint.CENTER -> y - textHeight / 2
+ }
+ }
+
+ fun getScaled(length: Int, real: Float): Double {
+ val scale = length / 100.0
+ return real / scale
+ }
+
+ fun getReal(length: Int, scaled: Double): Float {
+ val scale = length / 100.0
+ return (scaled * scale).toFloat()
+ }
+
+ fun isPositionOutOfBounds(x: Double, y: Double): Boolean {
+ val tooBig = 100 < x || 100 < y
+ val tooSmall = 0 > x || 0 > y
+ return tooBig || tooSmall
+ }
+
+}
diff --git a/src/main/java/net/moonleay/gimbal/datagen/En_us_GimbalLanguageProvider.kt b/src/main/java/net/moonleay/gimbal/datagen/En_us_GimbalLanguageProvider.kt
index 34d0fce..0082df9 100644
--- a/src/main/java/net/moonleay/gimbal/datagen/En_us_GimbalLanguageProvider.kt
+++ b/src/main/java/net/moonleay/gimbal/datagen/En_us_GimbalLanguageProvider.kt
@@ -26,13 +26,25 @@ class En_us_GimbalLanguageProvider(dataGenerator: FabricDataGenerator?) :
override fun generateTranslations(translationBuilder: TranslationBuilder?) {
if (translationBuilder == null) return
+ /// Screens
+ // Gimbal settings screen
+ translationBuilder.add("gimbal.gui.settings", "Gimbal Settings")
+ translationBuilder.add("gimbal.gui.hud", "Show HUD")
+ translationBuilder.add("gimbal.gui.edithud", "Edit HUD Layout...")
+ translationBuilder.add("gimbal.gui.resethud", "Reset Layout")
+ translationBuilder.add("gimbal.gui.examplehud", "EXAMPLE [No Clip, Force]")
+
+
+ translationBuilder.add("gimbal.gui.enabled", "Enabled")
+ translationBuilder.add("gimbal.gui.disabled", "Disabled")
+
+ /// Options
// Editor modes
translationBuilder.add("gimbal.category.editormode", "Editor Modes")
translationBuilder.add("gimbal.key.editor.mode.insert", "Enter Insert Mode")
translationBuilder.add("gimbal.key.editor.mode.replace", "Enter Replace Mode")
translationBuilder.add("gimbal.key.editor.mode.visual", "Enter Visual Mode")
-
// Editor mode modifiers
translationBuilder.add("gimbal.category.editormodifier", "Editor Mode Modifiers")
translationBuilder.add("gimbal.key.editor.modifier.bulldozer", "Toggle Bulldozer Modifier")
diff --git a/src/main/java/net/moonleay/gimbal/mixin/YouInjectButtonMixin.java b/src/main/java/net/moonleay/gimbal/mixin/YouInjectButtonMixin.java
new file mode 100644
index 0000000..861ecd3
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/mixin/YouInjectButtonMixin.java
@@ -0,0 +1,66 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.mixin;
+
+import net.minecraft.client.gui.screen.GameMenuScreen;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.widget.TexturedButtonWidget;
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
+import net.moonleay.gimbal.build.BuildConstants;
+import net.moonleay.gimbal.client.ClientMain;
+import net.moonleay.gimbal.client.screen.GimbalSettingsGui;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(GameMenuScreen.class)
+// What is my purpose?
+public class YouInjectButtonMixin extends Screen {
+ // Go my god.
+
+ @Unique
+ private static final Identifier GIMBAL_TEXTURE = new Identifier(BuildConstants.modId, "textures/gimbal_options_texture_button.png");
+
+ protected YouInjectButtonMixin(Text title) {
+ super(title);
+ }
+
+ @Inject(method = "initWidgets", at = @At(value = "RETURN"))
+ private void func(CallbackInfo ci) {
+ this.addDrawableChild(
+ new TexturedButtonWidget(
+ this.width / 2 - 124,
+ this.height / 4 + 96 + -16,
+ 20,
+ 20,
+ 0,
+ 0,
+ 20,
+ GIMBAL_TEXTURE,
+ 20,
+ 40,
+ button -> this.client.setScreen(new GimbalSettingsGui(this, ClientMain.CONFIG)),
+ Text.translatable("gimbal.gui.settings")
+ )
+ );
+ }
+}
diff --git a/src/main/java/net/moonleay/gimbal/mixin/YouInjectButtonMixin2.java b/src/main/java/net/moonleay/gimbal/mixin/YouInjectButtonMixin2.java
new file mode 100644
index 0000000..70af1f4
--- /dev/null
+++ b/src/main/java/net/moonleay/gimbal/mixin/YouInjectButtonMixin2.java
@@ -0,0 +1,65 @@
+/*
+ * Gimbal
+ * Copyright (C) 2024 moonleay
+ *
+ * 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 net.moonleay.gimbal.mixin;
+
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.TitleScreen;
+import net.minecraft.client.gui.widget.TexturedButtonWidget;
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
+import net.moonleay.gimbal.build.BuildConstants;
+import net.moonleay.gimbal.client.ClientMain;
+import net.moonleay.gimbal.client.screen.GimbalSettingsGui;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(TitleScreen.class)
+public class YouInjectButtonMixin2 extends Screen {
+
+ @Unique
+ private static final Identifier GIMBAL_TEXTURE = new Identifier(BuildConstants.modId, "textures/gimbal_options_texture_button.png");
+
+ protected YouInjectButtonMixin2(Text title) {
+ super(title);
+ }
+
+ @Inject(method = "init", at = @At(value = "RETURN"))
+ private void func(CallbackInfo ci) {
+ int l = this.height / 4 + 48;
+ this.addDrawableChild(
+ new TexturedButtonWidget(
+ this.width / 2 - 124 - 24,
+ l + 72 + 12,
+ 20,
+ 20,
+ 0,
+ 0,
+ 20,
+ GIMBAL_TEXTURE,
+ 20,
+ 40,
+ button -> this.client.setScreen(new GimbalSettingsGui(this, ClientMain.CONFIG)),
+ Text.translatable("gimbal.gui.settings")
+ )
+ );
+ }
+}
diff --git a/src/main/resources/assets/gimbal/textures/gimbal_options_texture_button.png b/src/main/resources/assets/gimbal/textures/gimbal_options_texture_button.png
new file mode 100644
index 0000000..e11bf08
Binary files /dev/null and b/src/main/resources/assets/gimbal/textures/gimbal_options_texture_button.png differ
diff --git a/src/main/resources/gimbal.mixins.json b/src/main/resources/gimbal.mixins.json
index adf557d..479b9ab 100644
--- a/src/main/resources/gimbal.mixins.json
+++ b/src/main/resources/gimbal.mixins.json
@@ -15,7 +15,9 @@
"BulldozerMixin2",
"HudMixin",
"NoClipCameraFixMixin",
- "NormalModeMixin"
+ "NormalModeMixin",
+ "YouInjectButtonMixin",
+ "YouInjectButtonMixin2"
],
"injectors": {
"defaultRequire": 1