From f254da43f4c2d6cb38b6c795ec1842f128488f54 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 2 Sep 2025 02:05:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=201.=E5=88=B6=E4=BD=9C=E4=B8=AD=E6=89=93?= =?UTF-8?q?=E5=BC=80=E4=BE=9B=E5=BA=94=E5=99=A8=E9=AB=98=E4=BA=AE=E5=AF=B9?= =?UTF-8?q?=E5=BA=94=E6=A0=B7=E6=9D=BF=202.=E4=BC=98=E5=8C=96=E9=AB=98?= =?UTF-8?q?=E4=BA=AE=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/PatternHighlightStore.java | 47 ++++++++ .../ae2/client/gui/AEBaseScreenMixin.java | 45 +++++--- .../client/gui/PatternProviderCloseMixin.java | 28 +++++ .../client/gui/GuiExPatternTerminalMixin.java | 85 +------------- .../CraftingMonitorOpenProviderC2SPacket.java | 4 + .../com/extendedae_plus/util/GuiUtil.java | 107 ++++++++++++++++++ .../resources/extendedae_plus.mixins.json | 2 + 7 files changed, 222 insertions(+), 96 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/content/PatternHighlightStore.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java diff --git a/src/main/java/com/extendedae_plus/content/PatternHighlightStore.java b/src/main/java/com/extendedae_plus/content/PatternHighlightStore.java new file mode 100644 index 0000000..6afba04 --- /dev/null +++ b/src/main/java/com/extendedae_plus/content/PatternHighlightStore.java @@ -0,0 +1,47 @@ +package com.extendedae_plus.content; + +import appeng.api.crafting.IPatternDetails; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +public final class PatternHighlightStore { + // 使用同步的 WeakHashMap 存储高亮状态,键为 IPatternDetails,值为 Boolean + private static final Map HIGHLIGHTS = Collections.synchronizedMap(new WeakHashMap<>()); + + // 私有构造方法,防止实例化 + private PatternHighlightStore() {} + + /** + * 设置指定 details 的高亮状态。 + * @param details 需要设置的 IPatternDetails 实例 + * @param highlighted 是否高亮 + */ + public static void setHighlight(IPatternDetails details, boolean highlighted) { + if (details == null) return; + if (highlighted) { + HIGHLIGHTS.put(details, Boolean.TRUE); // 设置为高亮 + } else { + HIGHLIGHTS.remove(details); // 移除高亮 + } + } + + /** + * 获取指定 details 的高亮状态。 + * @param details 需要查询的 IPatternDetails 实例 + * @return 是否高亮 + */ + public static boolean getHighlight(IPatternDetails details) { + if (details == null) return false; + Boolean v = HIGHLIGHTS.get(details); + return v != null && v; + } + + /** + * 清空所有高亮状态(在供应器界面关闭时调用)。 + */ + public static void clearAll() { + HIGHLIGHTS.clear(); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java index 880b6c0..0e857dc 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java @@ -1,5 +1,6 @@ package com.extendedae_plus.mixin.ae2.client.gui; +import appeng.api.crafting.PatternDetailsHelper; import appeng.api.stacks.AEKey; import appeng.client.Point; import appeng.client.gui.AEBaseScreen; @@ -12,12 +13,14 @@ import appeng.client.gui.style.Text; import appeng.client.gui.style.TextAlignment; import appeng.menu.slot.AppEngSlot; import com.extendedae_plus.api.ExPatternPageAccessor; +import com.extendedae_plus.content.PatternHighlightStore; import com.extendedae_plus.network.CraftingMonitorJumpC2SPacket; import com.extendedae_plus.network.CraftingMonitorOpenProviderC2SPacket; import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.util.GuiUtil; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import com.mojang.logging.LogUtils; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.Rect2i; @@ -150,40 +153,49 @@ public abstract class AEBaseScreenMixin { @Inject(method = "renderSlot", at = @At("TAIL")) private void eap$renderSlotAmounts(GuiGraphics guiGraphics, Slot s, CallbackInfo ci) { Object self = this; - + // 只处理AppEngSlot类型的槽位 if (!(s instanceof AppEngSlot appEngSlot)) { return; } - + // 检查槽位是否可见且有效 if (!appEngSlot.isActive() || !appEngSlot.isSlotEnabled()) { return; } - + // 获取槽位中的物品 var itemStack = appEngSlot.getItem(); if (itemStack.isEmpty()) { return; } - + // 使用GuiUtil的格式化方法获取数量文本 String amountText = GuiUtil.getPatternOutputText(itemStack); if (amountText.isEmpty()) { return; } - + // 在槽位右下角绘制数量文本 Font font = eap$getFont(self); GuiUtil.drawAmountText(guiGraphics, font, amountText, appEngSlot.x, appEngSlot.y, 0.6f); + + try { + var details = PatternDetailsHelper.decodePattern(itemStack, Minecraft.getInstance().level, false); + if (PatternHighlightStore.getHighlight(details)) { + try { + GuiUtil.drawSlotRainbowHighlight(guiGraphics, s.x, s.y); + } catch (Throwable ignored) {} + } + } catch (Throwable ignore) {} } // 在 AEBaseScreen.drawText 完成某个文本绘制后,若该文本为“样板”标签,则紧接着绘制页码。 @Inject(method = "drawText", at = @At("TAIL"), remap = false) private void eap$appendPageAfterPatternsLabel(GuiGraphics guiGraphics, - Text text, - @Nullable TextOverride override, - CallbackInfo ci) { + Text text, + @Nullable TextOverride override, + CallbackInfo ci) { Object self = this; if (!(self instanceof GuiExPatternProvider)) { return; @@ -237,8 +249,10 @@ public abstract class AEBaseScreenMixin { if (!isPatterns) { String label = content.getString(); if (label != null) { - if (label.equals(Component.translatable("gui.pattern_provider.patterns").getString())) isPatterns = true; - else if (label.equals(Component.translatable("gui.extendedae.patterns").getString())) isPatterns = true; + if (label.equals(Component.translatable("gui.pattern_provider.patterns").getString())) + isPatterns = true; + else if (label.equals(Component.translatable("gui.extendedae.patterns").getString())) + isPatterns = true; else if (label.equals(Component.translatable("gui.ae2.patterns").getString())) isPatterns = true; } } @@ -263,16 +277,18 @@ public abstract class AEBaseScreenMixin { if (v instanceof Integer i) { max = Math.max(1, i); } - } catch (Throwable ignored) {} + } catch (Throwable ignored) { + } - String pageText = "第"+cur+"页" + "/" + max + "页"; + String pageText = "第" + cur + "页" + "/" + max + "页"; ScreenStyle style = eap$getStyle(self); int color = 0xFFFFFFFF; if (style != null) { try { color = style.getColor(PaletteColor.DEFAULT_TEXT_COLOR).toARGB(); - } catch (Throwable ignored) {} + } catch (Throwable ignored) { + } } int padding = 4; if (scale == 1.0f) { @@ -284,6 +300,7 @@ public abstract class AEBaseScreenMixin { guiGraphics.drawString(font, pageText, lineWidth + padding, 0, color, false); guiGraphics.pose().popPose(); } - } catch (Throwable ignored) {} + } catch (Throwable ignored) { + } } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java new file mode 100644 index 0000000..eed7a29 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java @@ -0,0 +1,28 @@ +package com.extendedae_plus.mixin.ae2.client.gui; + +import appeng.client.gui.implementations.PatternProviderScreen; +import com.extendedae_plus.content.PatternHighlightStore; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.world.inventory.AbstractContainerMenu; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(value = AbstractContainerScreen.class, remap = false) +public class PatternProviderCloseMixin { + + @Shadow + protected AbstractContainerMenu menu; + + @Inject(method = "removed", at = @At("HEAD")) + private void onRemoved(CallbackInfo ci) { + try { + if (((Object) this) instanceof PatternProviderScreen) { + PatternHighlightStore.clearAll(); + } + } catch (Throwable ignored) { + } + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java index 819d78f..e8b5402 100644 --- a/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java @@ -4,7 +4,6 @@ import appeng.api.crafting.PatternDetailsHelper; import appeng.client.gui.AEBaseScreen; import appeng.client.gui.Icon; import appeng.client.gui.me.patternaccess.PatternContainerRecord; -import appeng.client.gui.me.patternaccess.PatternSlot; import appeng.client.gui.style.ScreenStyle; import appeng.client.gui.widgets.AETextField; import appeng.client.gui.widgets.IconButton; @@ -13,6 +12,7 @@ import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor; import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.network.OpenProviderUiC2SPacket; +import com.extendedae_plus.util.GuiUtil; import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; @@ -76,41 +76,6 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen super(menu, playerInventory, title, style); } - @Unique - private static int eap$withAlpha(int rgb, int alpha255) { - return ((alpha255 & 0xFF) << 24) | (rgb & 0x00FFFFFF); - } - - /** - * 将 HSV 转换为 RGB(返回 0xRRGGBB,不含 alpha)。 - * h: 0.0~1.0,s: 0.0~1.0,v: 0.0~1.0 - */ - @Unique - private static int eap$hsvToRgb(float h, float s, float v) { - if (s <= 0.0f) { - int g = Math.round(v * 255.0f); - return (g << 16) | (g << 8) | g; - } - float hh = (h - (float) Math.floor(h)) * 6.0f; - int sector = (int) Math.floor(hh); - float f = hh - sector; - float p = v * (1.0f - s); - float q = v * (1.0f - s * f); - float t = v * (1.0f - s * (1.0f - f)); - float r, g, b; - switch (sector) { - case 0: r = v; g = t; b = p; break; - case 1: r = q; g = v; b = p; break; - case 2: r = p; g = v; b = t; break; - case 3: r = p; g = q; b = v; break; - case 4: r = t; g = p; b = v; break; - default: r = v; g = p; b = q; break; - } - int ri = Math.round(r * 255.0f); - int gi = Math.round(g * 255.0f); - int bi = Math.round(b * 255.0f); - return (ri << 16) | (gi << 8) | bi; - } /** * 获取当前选择的样板供应器ID @@ -579,53 +544,9 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen return; } - // 彩虹色的流转:基于时间在 HSV 色环上循环(4 秒为一周期) - long now = System.currentTimeMillis(); - final long rainbowPeriodMs = 4000L; - float hue = (now % rainbowPeriodMs) / (float) rainbowPeriodMs; // 0.0 ~ 1.0 - int rainbowRgb = eap$hsvToRgb(hue, 1.0f, 1.0f); + // 使用 GuiUtil 的通用绘制方法绘制槽位高亮(包含彩虹流转效果) + GuiUtil.drawPatternSlotHighlights(guiGraphics, this.menu.slots, this.matchedStack, this.matchedProvider); - for (Slot slot : this.menu.slots) { - if (!(slot instanceof PatternSlot ps)) { - continue; - } - - int sx = slot.x; - int sy = slot.y; - - boolean isMatchedSlot = this.matchedStack != null && this.matchedStack.contains(slot.getItem()); - boolean isMatchedProvider = false; - try { - PatternContainerRecord container = ps.getMachineInv(); - isMatchedProvider = this.matchedProvider != null && this.matchedProvider.contains(container); - } catch (Throwable ignored) { - } - - // 依据命中状态选择颜色方案 - int borderColor; - int backgroundColor; - - if (isMatchedSlot) { - // 命中槽位:使用彩虹色边框与浅底色(固定透明度,呈现色相流转效果) - borderColor = eap$withAlpha(rainbowRgb, 0xA0); - backgroundColor = eap$withAlpha(rainbowRgb, 0x3C); - } else if (!isMatchedProvider) { - borderColor = eap$withAlpha(0xFFFFFF, 0x40); - backgroundColor = eap$withAlpha(0x000000, 0x18); - } else { - borderColor = eap$withAlpha(0xFFFFFF, 0x30); - backgroundColor = eap$withAlpha(0xFFFFFF, 0x14); - } - - // 绘制 18x18 边框(1px 宽) - eap$fill(guiGraphics, new Rect2i(sx - 1, sy - 1, 18, 1), borderColor); - eap$fill(guiGraphics, new Rect2i(sx - 1, sy + 16, 18, 1), borderColor); - eap$fill(guiGraphics, new Rect2i(sx - 1, sy, 1, 16), borderColor); - eap$fill(guiGraphics, new Rect2i(sx + 16, sy, 1, 16), borderColor); - - // 绘制 16x16 浅底色(半透明,叠加在槽位上方) - eap$fill(guiGraphics, new Rect2i(sx, sy, 16, 16), backgroundColor); - } } @Unique diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java index b1329fe..20bfabf 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java @@ -11,6 +11,7 @@ import appeng.menu.AEBaseMenu; import appeng.menu.locator.MenuLocators; import appeng.menu.me.crafting.CraftingCPUMenu; import appeng.parts.AEBasePart; +import com.extendedae_plus.content.PatternHighlightStore; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; import com.mojang.logging.LogUtils; import net.minecraft.network.FriendlyByteBuf; @@ -99,6 +100,9 @@ public class CraftingMonitorOpenProviderC2SPacket { } else { host.openMenu(player, MenuLocators.forBlockEntity(pbe)); } + + PatternHighlightStore.setHighlight(pattern, true); + context.setPacketHandled(true); return; } catch (Throwable t) { diff --git a/src/main/java/com/extendedae_plus/util/GuiUtil.java b/src/main/java/com/extendedae_plus/util/GuiUtil.java index 2320665..1c804d9 100644 --- a/src/main/java/com/extendedae_plus/util/GuiUtil.java +++ b/src/main/java/com/extendedae_plus/util/GuiUtil.java @@ -2,11 +2,17 @@ package com.extendedae_plus.util; import appeng.api.crafting.PatternDetailsHelper; import appeng.api.stacks.GenericStack; +import appeng.client.gui.me.patternaccess.PatternContainerRecord; +import appeng.client.gui.me.patternaccess.PatternSlot; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; +import java.util.List; +import java.util.Set; + /** * GUI工具类,提供样板获取、绘制等通用功能 @@ -77,4 +83,105 @@ public class GuiUtil { guiGraphics.drawString(font, text, (int)(textX / scale), (int)(textY / scale), 0xFFFFFFFF, true); guiGraphics.pose().popPose(); } + + // Helper: add alpha channel to RGB (rgb is 0xRRGGBB) + private static int withAlpha(int rgb, int alpha255) { + return ((alpha255 & 0xFF) << 24) | (rgb & 0x00FFFFFF); + } + + // HSV -> RGB (returns 0xRRGGBB) + private static int hsvToRgb(float h, float s, float v) { + if (s <= 0.0f) { + int g = Math.round(v * 255.0f); + return (g << 16) | (g << 8) | g; + } + float hh = (h - (float) Math.floor(h)) * 6.0f; + int sector = (int) Math.floor(hh); + float f = hh - sector; + float p = v * (1.0f - s); + float q = v * (1.0f - s * f); + float t = v * (1.0f - s * (1.0f - f)); + float r, g, b; + switch (sector) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + default: r = v; g = p; b = q; break; + } + int ri = Math.round(r * 255.0f); + int gi = Math.round(g * 255.0f); + int bi = Math.round(b * 255.0f); + return (ri << 16) | (gi << 8) | bi; + } + + // 返回当前时间对应的彩虹色(0xRRGGBB),周期固定为 4000ms + private static int getRainbowRgb() { + long now = System.currentTimeMillis(); + final long rainbowPeriodMs = 4000L; + float hue = (now % rainbowPeriodMs) / (float) rainbowPeriodMs; // 0.0 ~ 1.0 + return hsvToRgb(hue, 1.0f, 1.0f); + } + + // 在给定槽位坐标绘制 1px 边框(18x18)和 16x16 半透明背景 + private static void drawSlotBox(GuiGraphics guiGraphics, int sx, int sy, int borderColor, int backgroundColor) { + guiGraphics.fill(sx - 1, sy - 1, sx + 17, sy, borderColor); + guiGraphics.fill(sx - 1, sy + 16, sx + 17, sy + 17, borderColor); + guiGraphics.fill(sx - 1, sy, sx, sy + 16, borderColor); + guiGraphics.fill(sx + 16, sy, sx + 17, sy + 16, borderColor); + guiGraphics.fill(sx, sy, sx + 16, sy + 16, backgroundColor); + } + + /** + * 在槽位上绘制彩色流转的高亮和浅底色 + */ + public static void drawPatternSlotHighlights(GuiGraphics guiGraphics, List slots, Set matchedStack, Set matchedProvider) { + if (slots == null) return; + + int rainbowRgb = getRainbowRgb(); + + for (Slot slot : slots) { + if (!(slot instanceof PatternSlot ps)) { + continue; + } + + int sx = slot.x; + int sy = slot.y; + + boolean isMatchedSlot = matchedStack != null && matchedStack.contains(slot.getItem()); + boolean isMatchedProvider = false; + try { + PatternContainerRecord container = ps.getMachineInv(); + isMatchedProvider = matchedProvider != null && matchedProvider.contains(container); + } catch (Throwable ignored) { + } + + int borderColor; + int backgroundColor; + + if (isMatchedSlot) { + borderColor = withAlpha(rainbowRgb, 0xA0); + backgroundColor = withAlpha(rainbowRgb, 0x3C); + } else if (!isMatchedProvider) { + borderColor = withAlpha(0xFFFFFF, 0x40); + backgroundColor = withAlpha(0x000000, 0x18); + } else { + borderColor = withAlpha(0xFFFFFF, 0x30); + backgroundColor = withAlpha(0xFFFFFF, 0x14); + } + + drawSlotBox(guiGraphics, sx, sy, borderColor, backgroundColor); + } + } + + /** + * 在指定槽位坐标绘制彩虹流转的边框与浅底色(用于非 PatternSlot 的高亮场景) + */ + public static void drawSlotRainbowHighlight(GuiGraphics guiGraphics, int sx, int sy) { + int rainbowRgb = getRainbowRgb(); + int borderColor = withAlpha(rainbowRgb, 0xA0); + int backgroundColor = withAlpha(rainbowRgb, 0x3C); + drawSlotBox(guiGraphics, sx, sy, borderColor, backgroundColor); + } } \ No newline at end of file diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index e001c30..5772e34 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -15,6 +15,7 @@ "ae2.accessor.PatternAccessTermScreenSlotsRowAccessor", "ae2.client.gui.AEBaseScreenMixin", "ae2.client.gui.PatternEncodingTermScreenMixin", + "ae2.client.gui.PatternProviderCloseMixin", "ae2.client.gui.PatternProviderScreenMixin", "ae2.client.gui.SlotGridLayoutMixin", "extendedae.accessor.GuiExPatternTerminalAccessor", @@ -30,6 +31,7 @@ "mixins": [ "ae2.AEProcessingPatternMixin", "ae2.CraftingCPUClusterMixin", + "ae2.IPatternDetailsMixin", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor",