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 index 4cf2900..0c07b5d 100644 --- a/src/main/java/com/extendedae_plus/client/gui/widgets/ScaledTextureButton.java +++ b/src/main/java/com/extendedae_plus/client/gui/widgets/ScaledTextureButton.java @@ -9,6 +9,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; public class ScaledTextureButton extends Button { + private final OnPress delegateOnPress; private final ResourceLocation texture; private final int textureWidth; private final int textureHeight; @@ -22,7 +23,9 @@ public class ScaledTextureButton extends Button { 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); + Component.empty(), btn -> { + }, DEFAULT_NARRATION); + this.delegateOnPress = onPress; this.texture = texture; this.textureWidth = textureWidth; this.textureHeight = textureHeight; @@ -41,6 +44,12 @@ public class ScaledTextureButton extends Button { this.active = visible; } + @Override + public void onPress() { + this.delegateOnPress.onPress(this); + this.setFocused(false); + } + @Override public void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { if (!this.visible) { @@ -51,19 +60,11 @@ public class ScaledTextureButton extends Button { 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; + Icon bgIcon = isHovered() ? Icon.TOOLBAR_BUTTON_BACKGROUND_HOVER : 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); 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 index 6b8805f..1680ecb 100644 --- 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 @@ -27,6 +27,12 @@ public abstract class PatternEncodingTermScaleButtonsMixin @Unique private static final ResourceLocation EAP$SCALE_BUTTON_TEXTURE = ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "textures/gui/beizeng.png"); + @Unique + private static final ResourceLocation EAP$SWAP_OUTPUT_TEXTURE = + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "textures/gui/zhu_fu_qie_huan.png"); + @Unique + private static final ResourceLocation EAP$RESTORE_RATIO_TEXTURE = + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "textures/gui/huanyuan.png"); @Unique private ScaledTextureButton eap$mul2Button; @@ -40,6 +46,10 @@ public abstract class PatternEncodingTermScaleButtonsMixin private ScaledTextureButton eap$div3Button; @Unique private ScaledTextureButton eap$div5Button; + @Unique + private ScaledTextureButton eap$swapOutputsButton; + @Unique + private ScaledTextureButton eap$restoreRatioButton; @Inject(method = "init", at = @At("TAIL"), remap = false) private void eap$initScaleButtons(CallbackInfo ci) { @@ -60,6 +70,16 @@ public abstract class PatternEncodingTermScaleButtonsMixin ScaleEncodingPatternC2SPacket.Operation.DIV3); this.eap$div5Button = eap$createScaleButton(32, 16, "/5", ScaleEncodingPatternC2SPacket.Operation.DIV5); + this.eap$swapOutputsButton = eap$createStandaloneButton( + EAP$SWAP_OUTPUT_TEXTURE, + "主副切换", + ScaleEncodingPatternC2SPacket.Operation.SWAP_OUTPUTS + ); + this.eap$restoreRatioButton = eap$createStandaloneButton( + EAP$RESTORE_RATIO_TEXTURE, + "恢复比例", + ScaleEncodingPatternC2SPacket.Operation.RESTORE_RATIO + ); } eap$ensureAdded(this.eap$mul2Button); @@ -68,6 +88,8 @@ public abstract class PatternEncodingTermScaleButtonsMixin eap$ensureAdded(this.eap$div2Button); eap$ensureAdded(this.eap$div3Button); eap$ensureAdded(this.eap$div5Button); + eap$ensureAdded(this.eap$swapOutputsButton); + eap$ensureAdded(this.eap$restoreRatioButton); } @Inject(method = "containerTick", at = @At("TAIL"), remap = false) @@ -85,6 +107,8 @@ public abstract class PatternEncodingTermScaleButtonsMixin eap$ensureAdded(this.eap$div2Button); eap$ensureAdded(this.eap$div3Button); eap$ensureAdded(this.eap$div5Button); + eap$ensureAdded(this.eap$swapOutputsButton); + eap$ensureAdded(this.eap$restoreRatioButton); boolean visible = screen.getMenu().getMode() == EncodingMode.PROCESSING; this.eap$mul2Button.setVisibility(visible); @@ -93,6 +117,8 @@ public abstract class PatternEncodingTermScaleButtonsMixin this.eap$div2Button.setVisibility(visible); this.eap$div3Button.setVisibility(visible); this.eap$div5Button.setVisibility(visible); + this.eap$swapOutputsButton.setVisibility(visible); + this.eap$restoreRatioButton.setVisibility(visible); if (!visible) { return; @@ -108,6 +134,8 @@ public abstract class PatternEncodingTermScaleButtonsMixin eap$placeButton(this.eap$mul2Button, "cheng_2", bounds); eap$placeButton(this.eap$mul3Button, "cheng_3", bounds); eap$placeButton(this.eap$mul5Button, "cheng_5", bounds); + eap$placeButton(this.eap$swapOutputsButton, "zhu_fu_qie_huan", bounds); + eap$placeButton(this.eap$restoreRatioButton, "huan_yuan_mo_ren", bounds); } @Unique @@ -127,6 +155,23 @@ public abstract class PatternEncodingTermScaleButtonsMixin ); } + @Unique + private ScaledTextureButton eap$createStandaloneButton(ResourceLocation texture, String tooltipText, + ScaleEncodingPatternC2SPacket.Operation op) { + return new ScaledTextureButton( + texture, + 16, + 16, + 0, + 0, + 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; diff --git a/src/main/java/com/extendedae_plus/network/ScaleEncodingPatternC2SPacket.java b/src/main/java/com/extendedae_plus/network/ScaleEncodingPatternC2SPacket.java index ed8002e..1277039 100644 --- a/src/main/java/com/extendedae_plus/network/ScaleEncodingPatternC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/ScaleEncodingPatternC2SPacket.java @@ -2,6 +2,7 @@ package com.extendedae_plus.network; import appeng.api.stacks.GenericStack; import appeng.menu.me.items.PatternEncodingTermMenu; +import appeng.menu.slot.FakeSlot; import appeng.parts.encoding.EncodingMode; import appeng.util.ConfigInventory; import com.extendedae_plus.ExtendedAEPlus; @@ -15,7 +16,7 @@ import net.neoforged.neoforge.network.handling.IPayloadContext; public class ScaleEncodingPatternC2SPacket implements CustomPacketPayload { public enum Operation { - MUL2, DIV2, MUL3, DIV3, MUL5, DIV5 + MUL2, DIV2, MUL3, DIV3, MUL5, DIV5, SWAP_OUTPUTS, RESTORE_RATIO } public static final Type TYPE = new Type<>( @@ -49,10 +50,32 @@ public class ScaleEncodingPatternC2SPacket implements CustomPacketPayload { return; } + if (msg.op == Operation.SWAP_OUTPUTS) { + rotateProcessingOutputs(menu); + menu.broadcastChanges(); + return; + } + + if (msg.op == Operation.RESTORE_RATIO) { + var accessor = (PatternEncodingTermMenuAccessor) (Object) menu; + long gcd = computeSharedGcd(accessor.eap$getEncodedInputsInv(), accessor.eap$getEncodedOutputsInv()); + if (gcd <= 1L) { + return; + } + + var reducedOutputs = reduceInventory(accessor.eap$getEncodedOutputsInv(), gcd); + var reducedInputs = reduceInventory(accessor.eap$getEncodedInputsInv(), gcd); + applyScaled(accessor.eap$getEncodedOutputsInv(), reducedOutputs); + applyScaled(accessor.eap$getEncodedInputsInv(), reducedInputs); + menu.broadcastChanges(); + return; + } + int scale = switch (msg.op) { case MUL2, DIV2 -> 2; case MUL3, DIV3 -> 3; case MUL5, DIV5 -> 5; + default -> 1; }; boolean divide = switch (msg.op) { case DIV2, DIV3, DIV5 -> true; @@ -110,4 +133,85 @@ public class ScaleEncodingPatternC2SPacket implements CustomPacketPayload { } } } + + private static GenericStack[] reduceInventory(ConfigInventory inventory, long gcd) { + var result = new GenericStack[inventory.size()]; + for (int slot = 0; slot < inventory.size(); slot++) { + GenericStack stack = inventory.getStack(slot); + if (stack == null) { + continue; + } + result[slot] = new GenericStack(stack.what(), stack.amount() / gcd); + } + return result; + } + + private static long computeSharedGcd(ConfigInventory inputs, ConfigInventory outputs) { + long gcd = 0L; + gcd = updateGcd(gcd, inputs); + gcd = updateGcd(gcd, outputs); + return gcd; + } + + private static long updateGcd(long currentGcd, ConfigInventory inventory) { + long gcd = currentGcd; + for (int slot = 0; slot < inventory.size(); slot++) { + GenericStack stack = inventory.getStack(slot); + if (stack == null || stack.amount() <= 0L) { + continue; + } + gcd = gcd == 0L ? stack.amount() : gcd(gcd, stack.amount()); + if (gcd == 1L) { + return 1L; + } + } + return gcd; + } + + private static long gcd(long a, long b) { + long left = Math.abs(a); + long right = Math.abs(b); + while (right != 0L) { + long temp = left % right; + left = right; + right = temp; + } + return left; + } + + private static void rotateProcessingOutputs(PatternEncodingTermMenu menu) { + FakeSlot[] outputSlots = menu.getProcessingOutputSlots(); + int nonEmptyCount = 0; + for (FakeSlot slot : outputSlots) { + if (!slot.getItem().isEmpty()) { + nonEmptyCount++; + } + } + if (nonEmptyCount < 2) { + return; + } + + var newOutputs = new net.minecraft.world.item.ItemStack[outputSlots.length]; + for (int i = 0; i < outputSlots.length; i++) { + newOutputs[i] = outputSlots[i].getItem().copy(); + } + + for (int i = 0; i < outputSlots.length; i++) { + if (outputSlots[i].getItem().isEmpty()) { + continue; + } + + for (int j = 1; j < outputSlots.length; j++) { + var nextItem = outputSlots[(i + j) % outputSlots.length].getItem(); + if (!nextItem.isEmpty()) { + newOutputs[i] = nextItem.copy(); + break; + } + } + } + + for (int i = 0; i < newOutputs.length; i++) { + outputSlots[i].set(newOutputs[i]); + } + } }