diff --git a/src/main/java/com/extendedae_plus/client/gui/widgets/ScaledTextureButton.java b/src/main/java/com/extendedae_plus/client/gui/widgets/ScaledTextureButton.java new file mode 100644 index 0000000..4cf2900 --- /dev/null +++ b/src/main/java/com/extendedae_plus/client/gui/widgets/ScaledTextureButton.java @@ -0,0 +1,86 @@ +package com.extendedae_plus.client.gui.widgets; + +import appeng.client.gui.Icon; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +public class ScaledTextureButton extends Button { + private final ResourceLocation texture; + private final int textureWidth; + private final int textureHeight; + private final int srcX; + private final int srcY; + private final int srcWidth; + private final int srcHeight; + private final float scale; + + public ScaledTextureButton(ResourceLocation texture, int textureWidth, int textureHeight, + int srcX, int srcY, int srcWidth, int srcHeight, float scale, + Component tooltipText, OnPress onPress) { + super(0, 0, Math.round(srcWidth * scale), Math.round(srcHeight * scale), + Component.empty(), onPress, DEFAULT_NARRATION); + this.texture = texture; + this.textureWidth = textureWidth; + this.textureHeight = textureHeight; + this.srcX = srcX; + this.srcY = srcY; + this.srcWidth = srcWidth; + this.srcHeight = srcHeight; + this.scale = scale; + if (tooltipText != null) { + this.setTooltip(Tooltip.create(tooltipText)); + } + } + + public void setVisibility(boolean visible) { + this.visible = visible; + this.active = visible; + } + + @Override + public void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + if (!this.visible) { + return; + } + + this.width = Math.round(this.srcWidth * this.scale); + this.height = Math.round(this.srcHeight * this.scale); + + int yOffset = this.isHovered() ? 1 : 0; + Icon bgIcon = isHovered() ? Icon.TOOLBAR_BUTTON_BACKGROUND_HOVER + : isFocused() ? Icon.TOOLBAR_BUTTON_BACKGROUND_FOCUS : Icon.TOOLBAR_BUTTON_BACKGROUND; + + RenderSystem.disableDepthTest(); + RenderSystem.enableBlend(); + + if (isFocused()) { + guiGraphics.fill(getX() - 1, getY() - 1, getX() + width + 1, getY(), 0xFFFFFFFF); + guiGraphics.fill(getX() - 1, getY(), getX(), getY() + height, 0xFFFFFFFF); + guiGraphics.fill(getX() + width, getY(), getX() + width + 1, getY() + height, 0xFFFFFFFF); + guiGraphics.fill(getX() - 1, getY() + height, getX() + width + 1, getY() + height + 1, 0xFFFFFFFF); + } + + var pose = guiGraphics.pose(); + pose.pushPose(); + pose.translate(getX(), getY() + yOffset, 0.0F); + pose.scale(scale, scale, 1.0F); + + bgIcon.getBlitter().dest(0, 0).zOffset(2).blit(guiGraphics); + + if (!this.active) { + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 0.5F); + } + guiGraphics.blit(this.texture, 0, 0, this.srcX, this.srcY, this.srcWidth, this.srcHeight, + this.textureWidth, this.textureHeight); + if (!this.active) { + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + } + + pose.popPose(); + RenderSystem.enableDepthTest(); + } +} diff --git a/src/main/java/com/extendedae_plus/init/ModNetwork.java b/src/main/java/com/extendedae_plus/init/ModNetwork.java index 31534f0..694efb9 100644 --- a/src/main/java/com/extendedae_plus/init/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/init/ModNetwork.java @@ -11,6 +11,8 @@ public class ModNetwork { public static void registerPayloadHandlers(final RegisterPayloadHandlersEvent event) { var registrar = event.registrar(ExtendedAEPlus.MODID); registrar.playToServer(ScalePatternsC2SPacket.TYPE, ScalePatternsC2SPacket.STREAM_CODEC, ScalePatternsC2SPacket::handle); + registrar.playToServer(ScaleEncodingPatternC2SPacket.TYPE, ScaleEncodingPatternC2SPacket.STREAM_CODEC, + ScaleEncodingPatternC2SPacket::handle); registrar.playToServer(InterfaceAdjustConfigAmountC2SPacket.TYPE, InterfaceAdjustConfigAmountC2SPacket.STREAM_CODEC, InterfaceAdjustConfigAmountC2SPacket::handle); registrar.playToClient(SetPatternHighlightS2CPacket.TYPE, SetPatternHighlightS2CPacket.STREAM_CODEC, SetPatternHighlightS2CPacket::handle); registrar.playToClient(ProvidersListS2CPacket.TYPE, ProvidersListS2CPacket.STREAM_CODEC, ProvidersListS2CPacket::handle); diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternEncodingTermMenuAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternEncodingTermMenuAccessor.java index 908962c..9537df1 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternEncodingTermMenuAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternEncodingTermMenuAccessor.java @@ -2,6 +2,7 @@ package com.extendedae_plus.mixin.ae2.accessor; import appeng.menu.me.items.PatternEncodingTermMenu; import appeng.menu.slot.RestrictedInputSlot; +import appeng.util.ConfigInventory; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @@ -12,4 +13,10 @@ public interface PatternEncodingTermMenuAccessor { @Accessor("blankPatternSlot") RestrictedInputSlot eap$getBlankPatternSlot(); + + @Accessor("encodedInputsInv") + ConfigInventory eap$getEncodedInputsInv(); + + @Accessor("encodedOutputsInv") + ConfigInventory eap$getEncodedOutputsInv(); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScaleButtonsMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScaleButtonsMixin.java new file mode 100644 index 0000000..6b8805f --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScaleButtonsMixin.java @@ -0,0 +1,160 @@ +package com.extendedae_plus.mixin.ae2.client.gui; + +import appeng.client.gui.AEBaseScreen; +import appeng.client.gui.me.items.PatternEncodingTermScreen; +import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.style.WidgetStyle; +import appeng.menu.AEBaseMenu; +import appeng.parts.encoding.EncodingMode; +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.client.gui.widgets.ScaledTextureButton; +import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor; +import com.extendedae_plus.mixin.accessor.ScreenAccessor; +import com.extendedae_plus.mixin.ae2.accessor.AEBaseScreenAccessor; +import com.extendedae_plus.network.ScaleEncodingPatternC2SPacket; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.client.renderer.Rect2i; +import net.neoforged.neoforge.network.PacketDistributor; +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(value = AEBaseScreen.class, remap = false) +public abstract class PatternEncodingTermScaleButtonsMixin { + @Unique + private static final ResourceLocation EAP$SCALE_BUTTON_TEXTURE = + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "textures/gui/beizeng.png"); + + @Unique + private ScaledTextureButton eap$mul2Button; + @Unique + private ScaledTextureButton eap$mul3Button; + @Unique + private ScaledTextureButton eap$mul5Button; + @Unique + private ScaledTextureButton eap$div2Button; + @Unique + private ScaledTextureButton eap$div3Button; + @Unique + private ScaledTextureButton eap$div5Button; + + @Inject(method = "init", at = @At("TAIL"), remap = false) + private void eap$initScaleButtons(CallbackInfo ci) { + if (!(((Object) this) instanceof PatternEncodingTermScreen)) { + return; + } + + if (this.eap$mul2Button == null) { + this.eap$mul2Button = eap$createScaleButton(0, 0, "x2", + ScaleEncodingPatternC2SPacket.Operation.MUL2); + this.eap$mul3Button = eap$createScaleButton(16, 0, "x3", + ScaleEncodingPatternC2SPacket.Operation.MUL3); + this.eap$mul5Button = eap$createScaleButton(32, 0, "x5", + ScaleEncodingPatternC2SPacket.Operation.MUL5); + this.eap$div2Button = eap$createScaleButton(0, 16, "/2", + ScaleEncodingPatternC2SPacket.Operation.DIV2); + this.eap$div3Button = eap$createScaleButton(16, 16, "/3", + ScaleEncodingPatternC2SPacket.Operation.DIV3); + this.eap$div5Button = eap$createScaleButton(32, 16, "/5", + ScaleEncodingPatternC2SPacket.Operation.DIV5); + } + + eap$ensureAdded(this.eap$mul2Button); + eap$ensureAdded(this.eap$mul3Button); + eap$ensureAdded(this.eap$mul5Button); + eap$ensureAdded(this.eap$div2Button); + eap$ensureAdded(this.eap$div3Button); + eap$ensureAdded(this.eap$div5Button); + } + + @Inject(method = "containerTick", at = @At("TAIL"), remap = false) + private void eap$updateScaleButtons(CallbackInfo ci) { + if (!(((Object) this) instanceof PatternEncodingTermScreen screen)) { + return; + } + if (this.eap$mul2Button == null) { + return; + } + + eap$ensureAdded(this.eap$mul2Button); + eap$ensureAdded(this.eap$mul3Button); + eap$ensureAdded(this.eap$mul5Button); + eap$ensureAdded(this.eap$div2Button); + eap$ensureAdded(this.eap$div3Button); + eap$ensureAdded(this.eap$div5Button); + + boolean visible = screen.getMenu().getMode() == EncodingMode.PROCESSING; + this.eap$mul2Button.setVisibility(visible); + this.eap$mul3Button.setVisibility(visible); + this.eap$mul5Button.setVisibility(visible); + this.eap$div2Button.setVisibility(visible); + this.eap$div3Button.setVisibility(visible); + this.eap$div5Button.setVisibility(visible); + + if (!visible) { + return; + } + + Rect2i bounds = eap$getScreenBounds(); + if (bounds == null) { + return; + } + eap$placeButton(this.eap$div2Button, "chu_2", bounds); + eap$placeButton(this.eap$div3Button, "chu_3", bounds); + eap$placeButton(this.eap$div5Button, "chu_5", bounds); + eap$placeButton(this.eap$mul2Button, "cheng_2", bounds); + eap$placeButton(this.eap$mul3Button, "cheng_3", bounds); + eap$placeButton(this.eap$mul5Button, "cheng_5", bounds); + } + + @Unique + private ScaledTextureButton eap$createScaleButton(int srcX, int srcY, String tooltipText, + ScaleEncodingPatternC2SPacket.Operation op) { + return new ScaledTextureButton( + EAP$SCALE_BUTTON_TEXTURE, + 48, + 32, + srcX, + srcY, + 16, + 16, + 0.375f, + Component.literal(tooltipText), + btn -> PacketDistributor.sendToServer(new ScaleEncodingPatternC2SPacket(op)) + ); + } + + @Unique + private void eap$ensureAdded(ScaledTextureButton button) { + var accessor = (ScreenAccessor) (Object) this; + var renderables = accessor.eap$getRenderables(); + var children = accessor.eap$getChildren(); + if (!renderables.contains(button)) { + renderables.add(button); + } + if (!children.contains(button)) { + children.add(button); + } + } + + @Unique + private Rect2i eap$getScreenBounds() { + int leftPos = ((AbstractContainerScreenAccessor) (Object) this).eap$getLeftPos(); + int topPos = ((AbstractContainerScreenAccessor) (Object) this).eap$getTopPos(); + int imageWidth = ((AbstractContainerScreenAccessor) (Object) this).eap$getImageWidth(); + int imageHeight = ((AbstractContainerScreenAccessor) (Object) this).eap$getImageHeight(); + return new Rect2i(leftPos, topPos, imageWidth, imageHeight); + } + + @Unique + private void eap$placeButton(ScaledTextureButton button, String widgetId, Rect2i bounds) { + ScreenStyle style = ((AEBaseScreenAccessor) (Object) this).eap$getStyle(); + WidgetStyle widgetStyle = style.getWidget(widgetId); + var pos = widgetStyle.resolve(bounds); + button.setX(pos.getX()); + button.setY(pos.getY()); + } +} diff --git a/src/main/java/com/extendedae_plus/network/ScaleEncodingPatternC2SPacket.java b/src/main/java/com/extendedae_plus/network/ScaleEncodingPatternC2SPacket.java new file mode 100644 index 0000000..ed8002e --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/ScaleEncodingPatternC2SPacket.java @@ -0,0 +1,113 @@ +package com.extendedae_plus.network; + +import appeng.api.stacks.GenericStack; +import appeng.menu.me.items.PatternEncodingTermMenu; +import appeng.parts.encoding.EncodingMode; +import appeng.util.ConfigInventory; +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.mixin.ae2.accessor.PatternEncodingTermMenuAccessor; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +public class ScaleEncodingPatternC2SPacket implements CustomPacketPayload { + public enum Operation { + MUL2, DIV2, MUL3, DIV3, MUL5, DIV5 + } + + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "scale_encoding_pattern")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> buf.writeEnum(pkt.op), + buf -> new ScaleEncodingPatternC2SPacket(buf.readEnum(Operation.class)) + ); + + private final Operation op; + + public ScaleEncodingPatternC2SPacket(Operation op) { + this.op = op; + } + + @Override + public Type type() { + return TYPE; + } + + public static void handle(final ScaleEncodingPatternC2SPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (!(ctx.player() instanceof ServerPlayer player)) { + return; + } + if (!(player.containerMenu instanceof PatternEncodingTermMenu menu)) { + return; + } + if (menu.getMode() != EncodingMode.PROCESSING) { + return; + } + + int scale = switch (msg.op) { + case MUL2, DIV2 -> 2; + case MUL3, DIV3 -> 3; + case MUL5, DIV5 -> 5; + }; + boolean divide = switch (msg.op) { + case DIV2, DIV3, DIV5 -> true; + default -> false; + }; + + var accessor = (PatternEncodingTermMenuAccessor) (Object) menu; + var scaledOutputs = scaleInventory(accessor.eap$getEncodedOutputsInv(), scale, divide); + if (scaledOutputs == null) { + return; + } + + var scaledInputs = scaleInventory(accessor.eap$getEncodedInputsInv(), scale, divide); + if (scaledInputs == null) { + return; + } + + applyScaled(accessor.eap$getEncodedOutputsInv(), scaledOutputs); + applyScaled(accessor.eap$getEncodedInputsInv(), scaledInputs); + menu.broadcastChanges(); + }); + } + + private static GenericStack[] scaleInventory(ConfigInventory inventory, int scale, boolean divide) { + var result = new GenericStack[inventory.size()]; + for (int slot = 0; slot < inventory.size(); slot++) { + GenericStack stack = inventory.getStack(slot); + if (stack == null) { + continue; + } + + long nextAmount; + if (divide) { + if (stack.amount() % scale != 0) { + return null; + } + nextAmount = stack.amount() / scale; + } else { + try { + nextAmount = Math.multiplyExact(stack.amount(), scale); + } catch (ArithmeticException ex) { + return null; + } + } + + result[slot] = new GenericStack(stack.what(), nextAmount); + } + return result; + } + + private static void applyScaled(ConfigInventory inventory, GenericStack[] scaledStacks) { + for (int slot = 0; slot < scaledStacks.length; slot++) { + if (scaledStacks[slot] != null) { + inventory.setStack(slot, scaledStacks[slot]); + } + } + } +} diff --git a/src/main/resources/assets/ae2/screens/terminals/encoding/eaep_pattern_terminals.json b/src/main/resources/assets/ae2/screens/terminals/encoding/eaep_pattern_terminals.json new file mode 100644 index 0000000..ce56a4e --- /dev/null +++ b/src/main/resources/assets/ae2/screens/terminals/encoding/eaep_pattern_terminals.json @@ -0,0 +1,37 @@ +{ + "$schema": "../../schema.json", + "widgets": { + "zhu_fu_qie_huan": { + "left": 125, + "bottom": 159 + }, + "huan_yuan_mo_ren": { + "left": 101, + "bottom": 159 + }, + "cheng_2": { + "left": 125, + "bottom": 149 + }, + "cheng_3": { + "left": 125, + "bottom": 138 + }, + "cheng_5": { + "left": 125, + "bottom": 127 + }, + "chu_2": { + "left": 101, + "bottom": 149 + }, + "chu_3": { + "left": 101, + "bottom": 138 + }, + "chu_5": { + "left": 101, + "bottom": 127 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ae2/screens/terminals/pattern_encoding_terminal.json b/src/main/resources/assets/ae2/screens/terminals/pattern_encoding_terminal.json new file mode 100644 index 0000000..4c18cf1 --- /dev/null +++ b/src/main/resources/assets/ae2/screens/terminals/pattern_encoding_terminal.json @@ -0,0 +1,118 @@ +{ + "$schema": "../schema.json", + "includes": [ + "terminal.json", + "encoding/crafting.json", + "encoding/processing.json", + "encoding/smithing_table.json", + "encoding/stonecutting.json", + "encoding/eaep_pattern_terminals.json" + ], + "helpTopic": "items-blocks-machines/terminals.md#pattern-encoding-terminal-ui", + "slots": { + "BLANK_PATTERN": { + "left": 147, + "bottom": 165 + }, + "ENCODED_PATTERN": { + "left": 147, + "bottom": 118 + } + }, + "text": { + "dialog_title": { + "text": { + "translate": "gui.ae2.Terminal" + }, + "position": { + "left": 8, + "top": 6 + } + }, + "crafting_grid_title": { + "text": { + "translate": "gui.ae2.PatternEncoding" + }, + "position": { + "left": 8, + "bottom": 177 + } + } + }, + "terminalStyle": { + "header": { + "texture": "guis/pattern.png", + "srcRect": [0, 0, 195, 17] + }, + "firstRow": { + "texture": "guis/pattern.png", + "srcRect": [0, 17, 195, 18] + }, + "row": { + "texture": "guis/pattern.png", + "srcRect": [0, 35, 195, 18] + }, + "lastRow": { + "texture": "guis/pattern.png", + "srcRect": [0, 53, 195, 18] + }, + "bottom": { + "texture": "guis/pattern.png", + "srcRect": [0, 71, 195, 180] + } + }, + "widgets": { + "modeTabButton0": { + "left": 173, + "bottom": 174, + "width": 22, + "height": 22 + }, + "modeTabButton1": { + "left": 173, + "bottom": 153, + "width": 22, + "height": 22 + }, + "modeTabButton2": { + "left": 173, + "bottom": 132, + "width": 22, + "height": 22 + }, + "modeTabButton3": { + "left": 173, + "bottom": 111, + "width": 22, + "height": 22 + }, + "modePanel0": { + "left": 9, + "bottom": 166 + }, + "modePanel1": { + "left": 9, + "bottom": 166 + }, + "modePanel2": { + "left": 9, + "bottom": 166 + }, + "modePanel3": { + "left": 9, + "bottom": 166 + }, + "modePanel4": { + "left": 9, + "bottom": 166 + }, + "modePanel5": { + "left": 9, + "bottom": 166 + }, + "encodePattern": { + "left": 147, + "bottom": 145 + } + } +} diff --git a/src/main/resources/assets/extendedae_plus/textures/gui/beizeng.png b/src/main/resources/assets/extendedae_plus/textures/gui/beizeng.png new file mode 100644 index 0000000..820a4f7 Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/gui/beizeng.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/gui/huanyuan.png b/src/main/resources/assets/extendedae_plus/textures/gui/huanyuan.png new file mode 100644 index 0000000..e83ef33 Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/gui/huanyuan.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/gui/zhu_fu_qie_huan.png b/src/main/resources/assets/extendedae_plus/textures/gui/zhu_fu_qie_huan.png new file mode 100644 index 0000000..a619a96 Binary files /dev/null and b/src/main/resources/assets/extendedae_plus/textures/gui/zhu_fu_qie_huan.png differ diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 5068161..c78c995 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -80,6 +80,7 @@ "ae2.client.gui.AEBaseScreenMixin", "ae2.client.gui.InterfaceScreenMixin", "ae2.client.gui.PatternEncodingTermScreenMixin", + "ae2.client.gui.PatternEncodingTermScaleButtonsMixin", "ae2.client.gui.PatternEncodingTermUploadMixin", "ae2.client.gui.PatternProviderCloseMixin", "ae2.client.gui.PatternProviderScreenUpgradesMixin",