From 21817efdfe62135704571908b9f72ed3ace4bdf1 Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Wed, 17 Sep 2025 22:24:09 +0200 Subject: feat: add sign text & background movers --- gradle/libs.versions.toml | 2 +- .../mixins/accessor/AccessorScreenHandler.java | 12 ++++ src/main/resources/firmament.mixins.json | 3 + .../features/texturepack/CustomScreenLayouts.kt | 43 +++++++++++-- .../screenlayouts/MoveSignElements.java | 70 ++++++++++++++++++++++ web/src/pages/docs/_texture-pack-format.md | 5 ++ 6 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 src/main/java/moe/nea/firmament/mixins/accessor/AccessorScreenHandler.java create mode 100644 src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/MoveSignElements.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 707add4..a37c54c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -67,7 +67,7 @@ neurepoparser = "1.8.0" hotswap_agent = "1.4.2-SNAPSHOT" # Update from https://github.com/LlamaLad7/MixinExtras/tags -mixinextras = "0.4.1" +mixinextras = "0.5.0" # Update from https://repo.nea.moe/#/releases/moe/nea/jarvis/ jarvis = "2.0.0" diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorScreenHandler.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorScreenHandler.java new file mode 100644 index 0000000..a022d8e --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorScreenHandler.java @@ -0,0 +1,12 @@ +package moe.nea.firmament.mixins.accessor; + +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.ScreenHandlerType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ScreenHandler.class) +public interface AccessorScreenHandler { + @Accessor("type") + ScreenHandlerType getType_firmament(); +} diff --git a/src/main/resources/firmament.mixins.json b/src/main/resources/firmament.mixins.json index d78d124..c3392a6 100644 --- a/src/main/resources/firmament.mixins.json +++ b/src/main/resources/firmament.mixins.json @@ -4,6 +4,9 @@ "package": "moe.nea.firmament.mixins", "compatibilityLevel": "JAVA_21", "refmap": "Firmament-refmap.json", + "mixinextras": { + "minVersion": "0.5.0" + }, "injectors": { "defaultRequire": 1 } diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomScreenLayouts.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomScreenLayouts.kt index 69a1c7e..3554372 100644 --- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomScreenLayouts.kt +++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomScreenLayouts.kt @@ -7,11 +7,15 @@ import net.minecraft.client.font.TextRenderer import net.minecraft.client.gl.RenderPipelines import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen import net.minecraft.client.gui.screen.ingame.HandledScreen +import net.minecraft.client.gui.screen.ingame.HangingSignEditScreen +import net.minecraft.client.gui.screen.ingame.SignEditScreen import net.minecraft.client.render.RenderLayer import net.minecraft.registry.Registries import net.minecraft.resource.ResourceManager import net.minecraft.resource.SinglePreparationResourceReloader +import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.Slot import net.minecraft.text.Text import net.minecraft.util.Identifier @@ -24,6 +28,7 @@ import moe.nea.firmament.features.texturepack.CustomScreenLayouts.Alignment.CENT import moe.nea.firmament.features.texturepack.CustomScreenLayouts.Alignment.LEFT import moe.nea.firmament.features.texturepack.CustomScreenLayouts.Alignment.RIGHT import moe.nea.firmament.mixins.accessor.AccessorHandledScreen +import moe.nea.firmament.mixins.accessor.AccessorScreenHandler import moe.nea.firmament.util.ErrorUtil.intoCatch import moe.nea.firmament.util.IdentifierSerializer @@ -38,7 +43,13 @@ object CustomScreenLayouts : SinglePreparationResourceReloader? = null, + ) { + init { + if (signLines != null) + require(signLines.size == 4) + } + } @Serializable data class ComponentMover( @@ -56,11 +67,17 @@ object CustomScreenLayouts : SinglePreparationResourceReloader? ?: return false - val typeMatches = screenType == null || s.screenHandler.type.equals(Registries.SCREEN_HANDLER - .get(screenType)); + val type = when (screen) { + is HandledScreen<*> -> (screen.screenHandler as AccessorScreenHandler).type_firmament?.let { + Registries.SCREEN_HANDLER.getId(it) + } - return label.matches(s.title) && typeMatches + is HangingSignEditScreen -> Identifier.of("firmskyblock", "hanging_sign") + is SignEditScreen -> Identifier.of("firmskyblock", "sign") + else -> null + } + val typeMatches = screenType == null || type == screenType; + return label.matches(screen.title) && typeMatches } } @@ -74,6 +91,16 @@ object CustomScreenLayouts : SinglePreparationResourceReloader) { screen as AccessorHandledScreen val originalX: Int = (screen.width - screen.backgroundWidth_Firmament) / 2 @@ -197,9 +224,13 @@ object CustomScreenLayouts : SinglePreparationResourceReloadergetMover(selector: (CustomScreenLayout)-> (T?)) = + fun getMover(selector: (CustomScreenLayout) -> (T?)) = activeScreenOverride?.let(selector) + @JvmStatic + fun getSignTextMover(index: Int) = + getMover { it.signLines?.get(index) } + @JvmStatic fun getTextMover(selector: (CustomScreenLayout) -> (TitleReplacer?)) = getMover(selector) ?: DO_NOTHING_TEXT_REPLACER diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/MoveSignElements.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/MoveSignElements.java new file mode 100644 index 0000000..916a877 --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/screenlayouts/MoveSignElements.java @@ -0,0 +1,70 @@ +package moe.nea.firmament.mixins.custommodels.screenlayouts; + +import com.llamalad7.mixinextras.expression.Definition; +import com.llamalad7.mixinextras.expression.Expression; +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.Share; +import moe.nea.firmament.features.texturepack.CustomScreenLayouts; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(AbstractSignEditScreen.class) +public class MoveSignElements { + @WrapWithCondition( + method = "renderSign", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/AbstractSignEditScreen;renderSignBackground(Lnet/minecraft/client/gui/DrawContext;)V")) + private boolean onDrawBackgroundSign(AbstractSignEditScreen instance, DrawContext drawContext) { + final var override = CustomScreenLayouts.getActiveScreenOverride(); + if (override == null || override.getBackground() == null) return true; + override.getBackground().renderDirect(drawContext); + return false; + } + + @WrapOperation(method = "renderSignText", at = { + @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawSelection(IIII)V")} + ) + private void onRenderSignTextSelection( + DrawContext instance, int x1, int y1, int x2, int y2, Operation original, + @Local(index = 9) int messageIndex) { + instance.getMatrices().pushMatrix(); + final var override = CustomScreenLayouts.getSignTextMover(messageIndex); + if (override != null) { + instance.getMatrices().translate(override.getX(), override.getY()); + } + original.call(instance, x1, y1, x2, y2); + instance.getMatrices().popMatrix(); + } + @WrapOperation(method = "renderSignText", at = { + @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;fill(IIIII)V")} + ) + private void onRenderSignTextFill( + DrawContext instance, int x1, int y1, int x2, int y2, int color, Operation original, @Local(index = 9) int messageIndex) { + instance.getMatrices().pushMatrix(); + final var override = CustomScreenLayouts.getSignTextMover(messageIndex); + if (override != null) { + instance.getMatrices().translate(override.getX(), override.getY()); + } + original.call(instance, x1, y1, x2, y2, color); + instance.getMatrices().popMatrix(); + } + + @WrapOperation(method = "renderSignText", at = { + @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawText(Lnet/minecraft/client/font/TextRenderer;Ljava/lang/String;IIIZ)V")}, + expect = 2) + private void onRenderSignTextRendering(DrawContext instance, TextRenderer textRenderer, String text, int x, int y, int color, boolean shadow, Operation original, @Local(index = 9) int messageIndex) { + instance.getMatrices().pushMatrix(); + final var override = CustomScreenLayouts.getSignTextMover(messageIndex); + if (override != null) { + instance.getMatrices().translate(override.getX(), override.getY()); + } + original.call(instance, textRenderer, text, x, y, color, shadow); + instance.getMatrices().popMatrix(); + } + +} diff --git a/web/src/pages/docs/_texture-pack-format.md b/web/src/pages/docs/_texture-pack-format.md index 26274c2..8cbedbf 100644 --- a/web/src/pages/docs/_texture-pack-format.md +++ b/web/src/pages/docs/_texture-pack-format.md @@ -596,6 +596,8 @@ The `label` property is a regular [string matcher](#string-matcher) and matches The `screenType` property is an optional namespaced identifier that allows matching to a [screen type](https://minecraft.wiki/w/Java_Edition_protocol/Inventory#Types). +Signs can be targeted using `firmskyblock:sign` and `firmskyblock:hanging_sign`. + ### Changing the background ```json @@ -617,6 +619,8 @@ The `screenType` property is an optional namespaced identifier that allows match You need to specify an x and y offset relative to where the regular screen would render. This means you just check where the upper left corner of the UI texture would be in your texture (and turn it into a negative number). You also need to specify a width and height of your texture. This is the width in pixels rendered. If you want a higher or lower resolution texture, you can scale the actual texture up (tho it is expected to meet the same aspect ratio as the one defined here). +Signs do not have a regular origin and are instead anchored from the top-middle of the sign. + ### Moving slots around ```json @@ -691,6 +695,7 @@ Some other components can also be moved. These components might be buttons, text Available options - `nameField`: x, y, width & height are all available to move the field to set the name of the item in an anvil. +- `signLines[]`: x, y, are available to move the text relative to where it would render normally. Must be an array of 4 component movers. ### All together -- cgit