一、使用 pos+face 精确匹配并判空,修复同方块多贴片误选供应器问题
二、为ME扩展样板终端命中槽位加入18x18 边框+彩虹流转高亮槽位叠加 三、装配矩阵添加锻造台/切石机配方上传功能 四、对GuiExPatternTerminalMixin进行软依赖处理 五、添加工具类 六、对ae2和extendedae样板管理终端添加显示样板制作数量功能
This commit is contained in:
parent
0db35edb8f
commit
3ef34c106d
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -3,6 +3,7 @@
|
||||||
build/
|
build/
|
||||||
out/
|
out/
|
||||||
classes/
|
classes/
|
||||||
|
source/
|
||||||
|
|
||||||
# Eclipse
|
# Eclipse
|
||||||
*.tmp
|
*.tmp
|
||||||
|
|
|
||||||
|
|
@ -94,12 +94,9 @@ dependencies {
|
||||||
modCompileOnly "curse.maven:just-enough-characters-250702:6680042"
|
modCompileOnly "curse.maven:just-enough-characters-250702:6680042"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 忽略所有过时警告
|
allprojects {
|
||||||
gradle.projectsEvaluated {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
tasks.withType(JavaCompile).tap {
|
options.compilerArgs << "-Xlint:-deprecation"
|
||||||
configureEach {
|
|
||||||
options.compilerArgs << "-Xlint:-deprecation"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
|
||||||
/**
|
/**
|
||||||
* 给 AE2 的 PatternEncodingTermMenu 增加一个通用动作持有者,实现接收 EPP 的 CGenericPacket 动作。
|
* 给 AE2 的 PatternEncodingTermMenu 增加一个通用动作持有者,实现接收 EPP 的 CGenericPacket 动作。
|
||||||
* 注册动作 "upload_to_matrix":仅上传“合成图样”到 ExtendedAE 装配矩阵。
|
* 注册动作 "upload_to_matrix":仅上传“合成图样”到 ExtendedAE 装配矩阵。
|
||||||
|
|
@ -53,8 +52,7 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
|
||||||
eap$scheduleUploadWithRetry(sp, menu, attemptsLeft - 1);
|
eap$scheduleUploadWithRetry(sp, menu, attemptsLeft - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable ignored) {
|
||||||
LOGGER.error("Error uploading pattern to matrix", t);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -87,8 +85,10 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
|
||||||
return; // 仅服务器执行
|
return; // 仅服务器执行
|
||||||
}
|
}
|
||||||
var menu = (PatternEncodingTermMenu) (Object) this;
|
var menu = (PatternEncodingTermMenu) (Object) this;
|
||||||
if (menu.getMode() != EncodingMode.CRAFTING) {
|
if (menu.getMode() != EncodingMode.CRAFTING
|
||||||
return; // 只处理合成样板
|
&& menu.getMode() != EncodingMode.SMITHING_TABLE
|
||||||
|
&& menu.getMode() != EncodingMode.STONECUTTING) {
|
||||||
|
return; // 只处理合成/锻造台/切石机样板
|
||||||
}
|
}
|
||||||
if (this.encodedPatternSlot == null) {
|
if (this.encodedPatternSlot == null) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -107,8 +107,7 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Throwable t) {
|
} catch (Throwable ignored) {
|
||||||
LOGGER.error("Error uploading pattern to matrix", t);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.extendedae_plus.mixin.ae2;
|
||||||
|
|
||||||
|
import appeng.client.gui.me.patternaccess.PatternAccessTermScreen;
|
||||||
|
import com.extendedae_plus.util.GuiUtil;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
@Mixin(PatternAccessTermScreen.class)
|
||||||
|
public class PatternAccessTermScreenMixin {
|
||||||
|
// 在绘制前景的最后阶段叠加显示样板输出数量
|
||||||
|
@Inject(method = "drawFG", at = @At("TAIL"), remap = false)
|
||||||
|
private void injectDrawCraftingAmount(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX, int mouseY, CallbackInfo ci) {
|
||||||
|
PatternAccessTermScreen<?> screen = (PatternAccessTermScreen<?>)(Object) this;
|
||||||
|
|
||||||
|
// 调用GuiUtil的通用渲染方法
|
||||||
|
GuiUtil.renderPatternAmounts(guiGraphics, screen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.extendedae_plus.mixin.ae2.accessor;
|
||||||
|
|
||||||
|
import appeng.client.gui.me.patternaccess.PatternAccessTermScreen;
|
||||||
|
import appeng.client.gui.widgets.Scrollbar;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
@Mixin(value = PatternAccessTermScreen.class, remap = false)
|
||||||
|
public interface PatternAccessTermScreenAccessor {
|
||||||
|
@Accessor("scrollbar")
|
||||||
|
Scrollbar getScrollbar();
|
||||||
|
|
||||||
|
@Accessor("visibleRows")
|
||||||
|
int getVisibleRows();
|
||||||
|
|
||||||
|
@Accessor("rows")
|
||||||
|
ArrayList<?> getRows();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.extendedae_plus.mixin.ae2.accessor;
|
||||||
|
|
||||||
|
import appeng.client.gui.me.patternaccess.PatternContainerRecord;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
@Mixin(targets = "appeng.client.gui.me.patternaccess.PatternAccessTermScreen$SlotsRow", remap = false)
|
||||||
|
public interface PatternAccessTermScreenSlotsRowAccessor {
|
||||||
|
@Accessor("container")
|
||||||
|
PatternContainerRecord getContainer();
|
||||||
|
|
||||||
|
@Accessor("offset")
|
||||||
|
int getOffset();
|
||||||
|
|
||||||
|
@Accessor("slots")
|
||||||
|
int getSlots();
|
||||||
|
}
|
||||||
|
|
@ -3,55 +3,101 @@ package com.extendedae_plus.mixin.extendedae;
|
||||||
import appeng.api.crafting.PatternDetailsHelper;
|
import appeng.api.crafting.PatternDetailsHelper;
|
||||||
import appeng.client.gui.AEBaseScreen;
|
import appeng.client.gui.AEBaseScreen;
|
||||||
import appeng.client.gui.Icon;
|
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.style.ScreenStyle;
|
||||||
|
import appeng.client.gui.widgets.AETextField;
|
||||||
import appeng.client.gui.widgets.IconButton;
|
import appeng.client.gui.widgets.IconButton;
|
||||||
|
import appeng.menu.AEBaseMenu;
|
||||||
|
import com.extendedae_plus.util.GuiUtil;
|
||||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
||||||
import com.glodblock.github.extendedae.container.ContainerExPatternTerminal;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import com.glodblock.github.extendedae.network.EPPNetworkHandler;
|
|
||||||
import com.glodblock.github.glodium.network.packet.CGenericPacket;
|
|
||||||
import net.minecraft.client.gui.components.Tooltip;
|
import net.minecraft.client.gui.components.Tooltip;
|
||||||
|
import net.minecraft.client.renderer.Rect2i;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.inventory.Slot;
|
import net.minecraft.world.inventory.Slot;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Pseudo;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Pseudo
|
||||||
@Mixin(GuiExPatternTerminal.class)
|
@Mixin(GuiExPatternTerminal.class)
|
||||||
public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerExPatternTerminal> {
|
public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu> {
|
||||||
|
|
||||||
@Unique
|
|
||||||
private IconButton toggleSlotsButton;
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private boolean showSlots = false; // 默认显示槽位
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private long currentlychooicepatterprovider = -1; // 当前选择的样板供应器ID
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static final String UPLOAD_SUCCESS_MESSAGE = "✅ ExtendedAE Plus: 样板快速上传成功!";
|
private static final String UPLOAD_SUCCESS_MESSAGE = "✅ ExtendedAE Plus: 样板快速上传成功!";
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static final String UPLOAD_FAILED_MESSAGE = "❌ ExtendedAE Plus: 样板上传失败,请检查供应器状态";
|
private static final String UPLOAD_FAILED_MESSAGE = "❌ ExtendedAE Plus: 样板上传失败,请检查供应器状态";
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static final String NO_PROVIDER_MESSAGE = "ExtendedAE Plus: 请先选择一个样板供应器(点击GroupHeader旁的按钮)";
|
private static final String NO_PROVIDER_MESSAGE = "ExtendedAE Plus: 请先选择一个样板供应器(点击GroupHeader旁的按钮)";
|
||||||
|
@Unique
|
||||||
|
private IconButton eap$toggleSlotsButton;
|
||||||
|
@Unique
|
||||||
|
private boolean eap$showSlots = false; // 默认显示槽位
|
||||||
|
@Unique
|
||||||
|
private long eap$currentlyChoicePatterProvider = -1; // 当前选择的样板供应器ID
|
||||||
|
@Shadow(remap = false) private AETextField searchOutField;
|
||||||
|
@Shadow(remap = false) private AETextField searchInField;
|
||||||
|
@Shadow(remap = false) private Set<ItemStack> matchedStack;
|
||||||
|
@Shadow(remap = false) private Set<PatternContainerRecord> matchedProvider;
|
||||||
|
|
||||||
public GuiExPatternTerminalMixin(ContainerExPatternTerminal menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
public GuiExPatternTerminalMixin(AEBaseMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||||
super(menu, playerInventory, title, style);
|
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
|
* 获取当前选择的样板供应器ID
|
||||||
*/
|
*/
|
||||||
@Unique
|
@Unique
|
||||||
public long getCurrentlyChoicePatternProvider() {
|
public long getCurrentlyChoicePatternProvider() {
|
||||||
return currentlychooicepatterprovider;
|
return eap$currentlyChoicePatterProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -59,7 +105,7 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
||||||
*/
|
*/
|
||||||
@Unique
|
@Unique
|
||||||
public void setCurrentlyChoicePatternProvider(long id) {
|
public void setCurrentlyChoicePatternProvider(long id) {
|
||||||
this.currentlychooicepatterprovider = id;
|
this.eap$currentlyChoicePatterProvider = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,9 +124,9 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
||||||
// 检查是否是有效的编码样板
|
// 检查是否是有效的编码样板
|
||||||
if (!clickedItem.isEmpty() && PatternDetailsHelper.isEncodedPattern(clickedItem)) {
|
if (!clickedItem.isEmpty() && PatternDetailsHelper.isEncodedPattern(clickedItem)) {
|
||||||
// 检查是否选择了样板供应器
|
// 检查是否选择了样板供应器
|
||||||
if (currentlychooicepatterprovider != -1) {
|
if (eap$currentlyChoicePatterProvider != -1) {
|
||||||
// 执行快速上传
|
// 执行快速上传
|
||||||
this.quickUploadPattern(hoveredSlot.getSlotIndex());
|
this.eap$quickUploadPattern(hoveredSlot.getSlotIndex());
|
||||||
|
|
||||||
// 取消默认的点击行为
|
// 取消默认的点击行为
|
||||||
cir.setReturnValue(true);
|
cir.setReturnValue(true);
|
||||||
|
|
@ -88,8 +134,8 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
||||||
// 显示提示消息:请先选择一个样板供应器
|
// 显示提示消息:请先选择一个样板供应器
|
||||||
if (this.minecraft.player != null) {
|
if (this.minecraft.player != null) {
|
||||||
this.minecraft.player.displayClientMessage(
|
this.minecraft.player.displayClientMessage(
|
||||||
Component.literal("ExtendedAE Plus: 请先选择一个样板供应器(点击GroupHeader旁的按钮)"),
|
Component.literal("ExtendedAE Plus: 请先选择一个样板供应器(点击GroupHeader旁的按钮)"),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,20 +148,35 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
||||||
* 快速上传样板到当前选择的供应器
|
* 快速上传样板到当前选择的供应器
|
||||||
*/
|
*/
|
||||||
@Unique
|
@Unique
|
||||||
private void quickUploadPattern(int playerSlotIndex) {
|
private void eap$quickUploadPattern(int playerSlotIndex) {
|
||||||
if (this.minecraft.player != null) {
|
if (this.minecraft.player != null) {
|
||||||
// 获取要上传的物品
|
// 获取要上传的物品
|
||||||
ItemStack itemToUpload = this.minecraft.player.getInventory().getItem(playerSlotIndex);
|
ItemStack itemToUpload = this.minecraft.player.getInventory().getItem(playerSlotIndex);
|
||||||
|
|
||||||
if (!itemToUpload.isEmpty() && PatternDetailsHelper.isEncodedPattern(itemToUpload)) {
|
if (!itemToUpload.isEmpty() && PatternDetailsHelper.isEncodedPattern(itemToUpload)) {
|
||||||
// 通过 ExtendedAE 内置网络系统发送通用动作到服务端
|
// 通过反射调用 ExtendedAE 的网络发送(软依赖)
|
||||||
// 动作: "upload",参数: 槽位索引(int)、供应器ID(long)
|
try {
|
||||||
EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("upload", playerSlotIndex, currentlychooicepatterprovider));
|
Class<?> EPPNetworkHandlerClass = Class.forName("com.glodblock.github.extendedae.network.EPPNetworkHandler");
|
||||||
|
Object handlerInstance = EPPNetworkHandlerClass.getField("INSTANCE").get(null);
|
||||||
|
|
||||||
|
Class<?> packetClass = Class.forName("com.glodblock.github.glodium.network.packet.CGenericPacket");
|
||||||
|
Constructor<?> constructor = packetClass.getConstructor(String.class, Object[].class);
|
||||||
|
Object packet = constructor.newInstance("upload", new Object[]{playerSlotIndex, eap$currentlyChoicePatterProvider});
|
||||||
|
|
||||||
|
Class<?> iMessage = Class.forName("com.glodblock.github.glodium.network.packet.IMessage");
|
||||||
|
Method sendToServer = EPPNetworkHandlerClass.getMethod("sendToServer", iMessage);
|
||||||
|
|
||||||
|
sendToServer.invoke(handlerInstance, packet);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
this.minecraft.player.displayClientMessage(
|
||||||
|
Component.literal("❌ ExtendedAE Plus: 未找到 ExtendedAE 网络支持(可能未安装或版本不兼容)"),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.minecraft.player.displayClientMessage(
|
this.minecraft.player.displayClientMessage(
|
||||||
Component.literal("❌ ExtendedAE Plus: 无效的样板物品"),
|
Component.literal("❌ ExtendedAE Plus: 无效的样板物品"),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,14 +187,14 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
||||||
*/
|
*/
|
||||||
@Unique
|
@Unique
|
||||||
public void resetCurrentlyChoicePatternProvider() {
|
public void resetCurrentlyChoicePatternProvider() {
|
||||||
this.currentlychooicepatterprovider = -1;
|
this.eap$currentlyChoicePatterProvider = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("TAIL"), remap = false)
|
@Inject(method = "<init>", at = @At("TAIL"), remap = false)
|
||||||
private void injectConstructor(ContainerExPatternTerminal menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
private void injectConstructor(CallbackInfo ci) {
|
||||||
// 创建切换槽位显示的按钮
|
// 创建切换槽位显示的按钮
|
||||||
this.toggleSlotsButton = new IconButton((b) -> {
|
this.eap$toggleSlotsButton = new IconButton((b) -> {
|
||||||
this.showSlots = !this.showSlots; // 开关状态
|
this.eap$showSlots = !this.eap$showSlots; // 开关状态
|
||||||
|
|
||||||
// 通过反射调用refreshList方法 - 先尝试当前类,失败后尝试父类
|
// 通过反射调用refreshList方法 - 先尝试当前类,失败后尝试父类
|
||||||
try {
|
try {
|
||||||
|
|
@ -152,29 +213,28 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
||||||
|
|
||||||
refreshMethod.setAccessible(true);
|
refreshMethod.setAccessible(true);
|
||||||
refreshMethod.invoke(this);
|
refreshMethod.invoke(this);
|
||||||
} catch (Exception e) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@Override
|
@Override
|
||||||
protected Icon getIcon() {
|
protected Icon getIcon() {
|
||||||
return showSlots ? Icon.PATTERN_ACCESS_HIDE : Icon.PATTERN_ACCESS_SHOW;
|
return eap$showSlots ? Icon.PATTERN_ACCESS_HIDE : Icon.PATTERN_ACCESS_SHOW;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 设置按钮提示文本
|
// 设置按钮提示文本
|
||||||
this.toggleSlotsButton.setTooltip(Tooltip.create(Component.translatable("gui.expatternprovider.toggle_slots")));
|
this.eap$toggleSlotsButton.setTooltip(Tooltip.create(Component.translatable("gui.expatternprovider.toggle_slots")));
|
||||||
|
|
||||||
// 添加到左侧工具栏
|
// 添加到左侧工具栏
|
||||||
this.addToLeftToolbar(this.toggleSlotsButton);
|
this.addToLeftToolbar(this.eap$toggleSlotsButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "refreshList", at = @At("HEAD"), remap = false)
|
@Inject(method = "refreshList", at = @At("HEAD"), remap = false)
|
||||||
private void onRefreshListStart(CallbackInfo ci) {
|
private void onRefreshListStart(CallbackInfo ci) {
|
||||||
|
|
||||||
// 更新按钮图标
|
// 更新按钮图标
|
||||||
if (this.toggleSlotsButton != null) {
|
if (this.eap$toggleSlotsButton != null) {
|
||||||
this.toggleSlotsButton.setTooltip(Tooltip.create(Component.translatable(
|
this.eap$toggleSlotsButton.setTooltip(Tooltip.create(Component.translatable(
|
||||||
this.showSlots ? "gui.expatternprovider.hide_slots" : "gui.expatternprovider.show_slots"
|
this.eap$showSlots ? "gui.expatternprovider.hide_slots" : "gui.expatternprovider.show_slots"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +243,7 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
||||||
private void onRefreshListEnd(CallbackInfo ci) {
|
private void onRefreshListEnd(CallbackInfo ci) {
|
||||||
|
|
||||||
// 在refreshList结束后,根据showSlots状态过滤SlotsRow
|
// 在refreshList结束后,根据showSlots状态过滤SlotsRow
|
||||||
if (!this.showSlots) {
|
if (!this.eap$showSlots) {
|
||||||
try {
|
try {
|
||||||
// 通过反射访问rows字段 - 先尝试当前类,失败后尝试父类
|
// 通过反射访问rows字段 - 先尝试当前类,失败后尝试父类
|
||||||
java.lang.reflect.Field rowsField = null;
|
java.lang.reflect.Field rowsField = null;
|
||||||
|
|
@ -259,7 +319,7 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
||||||
|
|
||||||
// 强制刷新滚动条
|
// 强制刷新滚动条
|
||||||
try {
|
try {
|
||||||
java.lang.reflect.Method resetScrollbarMethod = null;
|
Method resetScrollbarMethod = null;
|
||||||
try {
|
try {
|
||||||
// 先尝试在当前类中查找
|
// 先尝试在当前类中查找
|
||||||
resetScrollbarMethod = this.getClass().getDeclaredMethod("resetScrollbar");
|
resetScrollbarMethod = this.getClass().getDeclaredMethod("resetScrollbar");
|
||||||
|
|
@ -274,11 +334,77 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
||||||
|
|
||||||
resetScrollbarMethod.setAccessible(true);
|
resetScrollbarMethod.setAccessible(true);
|
||||||
resetScrollbarMethod.invoke(this);
|
resetScrollbarMethod.invoke(this);
|
||||||
} catch (Exception e) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "drawFG", at = @At("TAIL"), remap = false)
|
||||||
|
private void eap$afterDrawFG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX, int mouseY, CallbackInfo ci) {
|
||||||
|
// 调用GuiUtil的通用渲染方法显示样板数量
|
||||||
|
GuiUtil.renderPatternAmounts(guiGraphics, this);
|
||||||
|
|
||||||
|
// 原有的搜索高亮逻辑
|
||||||
|
// 仅当任一搜索框非空时绘制叠加层(与原版行为保持一致)
|
||||||
|
boolean searchActive = (this.searchOutField != null && !this.searchOutField.getValue().isEmpty())
|
||||||
|
|| (this.searchInField != null && !this.searchInField.getValue().isEmpty());
|
||||||
|
if (!searchActive) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
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
|
||||||
|
private void eap$fill(GuiGraphics guiGraphics, Rect2i rect, int argb) {
|
||||||
|
this.fillRect(guiGraphics, rect, argb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,61 +3,70 @@ package com.extendedae_plus.mixin.extendedae;
|
||||||
import com.glodblock.github.extendedae.client.button.HighlightButton;
|
import com.glodblock.github.extendedae.client.button.HighlightButton;
|
||||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
||||||
import net.minecraft.client.gui.components.Button;
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
@Mixin(value = HighlightButton.class, priority = 1000)
|
@Mixin(value = HighlightButton.class, priority = 1000)
|
||||||
public abstract class HighlightButtonMixin {
|
public abstract class HighlightButtonMixin {
|
||||||
|
@Shadow(remap = false)
|
||||||
|
private static void highlight(Button btn) {}
|
||||||
|
|
||||||
@Shadow(remap = false)
|
private static final Logger LOGGER = LoggerFactory.getLogger("ExtendedAEPlus");
|
||||||
private static void highlight(Button btn) {}
|
|
||||||
|
|
||||||
@Inject(method = "highlight", at = @At("TAIL"), remap = false)
|
@Inject(method = "highlight", at = @At("TAIL"), remap = false)
|
||||||
private static void onHighlight(Button btn, CallbackInfo ci) {
|
private static void onHighlight(Button btn, CallbackInfo ci) {
|
||||||
if (btn instanceof HighlightButton hb) {
|
if (btn instanceof HighlightButton hb) {
|
||||||
// 获取当前打开的GUI屏幕
|
var minecraft = net.minecraft.client.Minecraft.getInstance();
|
||||||
var minecraft = net.minecraft.client.Minecraft.getInstance();
|
if (minecraft.screen instanceof GuiExPatternTerminal<?> terminal) {
|
||||||
if (minecraft.screen instanceof GuiExPatternTerminal<?> terminal) {
|
try {
|
||||||
// 通过反射获取HighlightButton的serverId信息
|
var fPos = HighlightButton.class.getDeclaredField("pos");
|
||||||
try {
|
fPos.setAccessible(true);
|
||||||
// 获取HighlightButton的pos字段,用于标识对应的样板供应器
|
Object btnPos = fPos.get(hb);
|
||||||
var posField = HighlightButton.class.getDeclaredField("pos");
|
if (btnPos == null) {
|
||||||
posField.setAccessible(true);
|
return;
|
||||||
var pos = posField.get(hb);
|
}
|
||||||
|
|
||||||
if (pos != null) {
|
var fFace = HighlightButton.class.getDeclaredField("face");
|
||||||
// 通过反射访问infoMap字段
|
fFace.setAccessible(true);
|
||||||
var infoMapField = GuiExPatternTerminal.class.getDeclaredField("infoMap");
|
Object btnFace = fFace.get(hb); // 允许为 null:方块形
|
||||||
infoMapField.setAccessible(true);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
var infoMap = (java.util.Map<Long, Object>) infoMapField.get(terminal);
|
|
||||||
|
|
||||||
// 查找对应的样板供应器ID
|
var infoMapField = GuiExPatternTerminal.class.getDeclaredField("infoMap");
|
||||||
for (var entry : infoMap.entrySet()) {
|
infoMapField.setAccessible(true);
|
||||||
var info = entry.getValue();
|
@SuppressWarnings("unchecked")
|
||||||
// 通过反射调用pos()方法
|
var infoMap = (java.util.Map<Long, Object>) infoMapField.get(terminal);
|
||||||
var posMethod = info.getClass().getMethod("pos");
|
|
||||||
var infoPos = posMethod.invoke(info);
|
|
||||||
|
|
||||||
if (pos.equals(infoPos)) {
|
for (var entry : infoMap.entrySet()) {
|
||||||
long serverId = entry.getKey();
|
var info = entry.getValue();
|
||||||
|
var mPos = info.getClass().getMethod("pos");
|
||||||
|
mPos.setAccessible(true);
|
||||||
|
Object infoPos = mPos.invoke(info);
|
||||||
|
|
||||||
// 通过反射调用setter方法
|
var mFace = info.getClass().getMethod("face");
|
||||||
try {
|
mFace.setAccessible(true);
|
||||||
var setMethod = terminal.getClass().getMethod("setCurrentlyChoicePatternProvider", long.class);
|
Object infoFace = mFace.invoke(info); // 允许为 null:方块形
|
||||||
setMethod.invoke(terminal, serverId);
|
|
||||||
} catch (Exception ignored) {
|
// 匹配规则:pos 必须相等;face 允许为 null,null 仅与 null 匹配
|
||||||
}
|
boolean posEqual = Objects.equals(btnPos, infoPos);
|
||||||
break;
|
boolean faceEqual = (btnFace == null && infoFace == null) || Objects.equals(btnFace, infoFace);
|
||||||
}
|
if (posEqual && faceEqual) {
|
||||||
}
|
long serverId = entry.getKey();
|
||||||
}
|
var setMethod = terminal.getClass().getMethod("setCurrentlyChoicePatternProvider", long.class);
|
||||||
} catch (Exception ignored) {
|
setMethod.setAccessible(true);
|
||||||
}
|
setMethod.invoke(terminal, serverId);
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
LOGGER.warn("HighlightButton onHighlight 处理异常", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.extendedae_plus.mixin.extendedae.accessor;
|
||||||
|
|
||||||
|
import appeng.client.gui.widgets.Scrollbar;
|
||||||
|
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
@Mixin(value = GuiExPatternTerminal.class, remap = false)
|
||||||
|
public interface GuiExPatternTerminalAccessor {
|
||||||
|
@Accessor("scrollbar")
|
||||||
|
Scrollbar getScrollbar();
|
||||||
|
|
||||||
|
@Accessor("visibleRows")
|
||||||
|
int getVisibleRows();
|
||||||
|
|
||||||
|
@Accessor("rows")
|
||||||
|
ArrayList<?> getRows();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.extendedae_plus.mixin.extendedae.accessor;
|
||||||
|
|
||||||
|
import appeng.client.gui.me.patternaccess.PatternContainerRecord;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
@Mixin(targets = "com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal$SlotsRow", remap = false)
|
||||||
|
public interface GuiExPatternTerminalSlotsRowAccessor {
|
||||||
|
@Accessor("container")
|
||||||
|
PatternContainerRecord getContainer();
|
||||||
|
|
||||||
|
@Accessor("offset")
|
||||||
|
int getOffset();
|
||||||
|
|
||||||
|
@Accessor("slots")
|
||||||
|
int getSlots();
|
||||||
|
}
|
||||||
13
src/main/java/com/extendedae_plus/util/ExtendedAELogger.java
Normal file
13
src/main/java/com/extendedae_plus/util/ExtendedAELogger.java
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.extendedae_plus.util;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global logger utility for ExtendedAE Plus mod
|
||||||
|
*/
|
||||||
|
public class ExtendedAELogger {
|
||||||
|
public static final Logger LOGGER = LoggerFactory.getLogger("ExtendedAEPlus");
|
||||||
|
|
||||||
|
private ExtendedAELogger() {throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,8 @@ import appeng.api.networking.IGrid;
|
||||||
import appeng.api.networking.IGridNode;
|
import appeng.api.networking.IGridNode;
|
||||||
import appeng.core.definitions.AEItems;
|
import appeng.core.definitions.AEItems;
|
||||||
import appeng.crafting.pattern.AECraftingPattern;
|
import appeng.crafting.pattern.AECraftingPattern;
|
||||||
|
import appeng.crafting.pattern.AESmithingTablePattern;
|
||||||
|
import appeng.crafting.pattern.AEStonecuttingPattern;
|
||||||
import appeng.helpers.patternprovider.PatternContainer;
|
import appeng.helpers.patternprovider.PatternContainer;
|
||||||
import appeng.menu.implementations.PatternAccessTermMenu;
|
import appeng.menu.implementations.PatternAccessTermMenu;
|
||||||
import appeng.menu.me.items.PatternEncodingTermMenu;
|
import appeng.menu.me.items.PatternEncodingTermMenu;
|
||||||
|
|
@ -433,10 +435,12 @@ public class ExtendedAEPatternUploadUtil {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 仅允许“合成图样”
|
// 仅允许“合成/锻造台/切石机图样”
|
||||||
IPatternDetails details = PatternDetailsHelper.decodePattern(stack, player.level());
|
IPatternDetails details = PatternDetailsHelper.decodePattern(stack, player.level());
|
||||||
if (!(details instanceof AECraftingPattern)) {
|
if (!(details instanceof AECraftingPattern
|
||||||
sendMessage(player, "extendedae_plus.upload_to_matrix.fail_not_crafting");
|
|| details instanceof AESmithingTablePattern
|
||||||
|
|| details instanceof AEStonecuttingPattern)) {
|
||||||
|
sendMessage(player, "extendedae_plus.upload_to_matrix.fail");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
176
src/main/java/com/extendedae_plus/util/GuiUtil.java
Normal file
176
src/main/java/com/extendedae_plus/util/GuiUtil.java
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
package com.extendedae_plus.util;
|
||||||
|
|
||||||
|
import appeng.api.crafting.PatternDetailsHelper;
|
||||||
|
import appeng.api.stacks.GenericStack;
|
||||||
|
import appeng.util.inv.AppEngInternalInventory;
|
||||||
|
import com.extendedae_plus.mixin.ae2.accessor.PatternAccessTermScreenAccessor;
|
||||||
|
import com.extendedae_plus.mixin.ae2.accessor.PatternAccessTermScreenSlotsRowAccessor;
|
||||||
|
import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor;
|
||||||
|
import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.Font;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GUI工具类,提供样板获取、绘制等通用功能
|
||||||
|
*/
|
||||||
|
public class GuiUtil {
|
||||||
|
private GuiUtil() {throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从样板中获取输出数量文本
|
||||||
|
*
|
||||||
|
* @param pattern 样板物品
|
||||||
|
* @return 格式化后的数量文本
|
||||||
|
*/
|
||||||
|
public static String getPatternOutputText(ItemStack pattern) {
|
||||||
|
if (pattern.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var details = PatternDetailsHelper.decodePattern(pattern, Minecraft.getInstance().level, false);
|
||||||
|
if (details == null || details.getOutputs().length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericStack out = details.getOutputs()[0];
|
||||||
|
long amount = out.amount();
|
||||||
|
long perUnit = out.what().getAmountPerUnit();
|
||||||
|
if (amount <= 0 || perUnit <= 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算实际单位数量,支持小数
|
||||||
|
double units = (double) amount / perUnit;
|
||||||
|
if (units <= 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动判断是否为流体,避免重复后缀
|
||||||
|
String autoSuffix = "";
|
||||||
|
if (perUnit > 1) {
|
||||||
|
// 如果每单位数量大于1,说明是流体(如1000mB = 1B)
|
||||||
|
autoSuffix = "B";
|
||||||
|
}
|
||||||
|
return NumberFormatUtil.formatNumberWithDecimal(units) + autoSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在槽位右下角绘制数量文本
|
||||||
|
* @param guiGraphics GUI图形上下文
|
||||||
|
* @param font 字体
|
||||||
|
* @param text 要绘制的文本
|
||||||
|
* @param slotX 槽位X坐标
|
||||||
|
* @param slotY 槽位Y坐标
|
||||||
|
* @param scale 缩放比例
|
||||||
|
*/
|
||||||
|
public static void drawAmountText(GuiGraphics guiGraphics, Font font, String text, int slotX, int slotY, float scale) {
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算缩放后的字体宽度,确保右对齐
|
||||||
|
int scaledWidth = (int)(font.width(text) * scale);
|
||||||
|
int textX = slotX + 16 - scaledWidth;
|
||||||
|
int textY = slotY + 11; // 右下角显示
|
||||||
|
|
||||||
|
guiGraphics.pose().pushPose();
|
||||||
|
guiGraphics.pose().translate(0, 0, 300); // 提升 Z,确保在最上层
|
||||||
|
guiGraphics.pose().scale(scale, scale, 1.0f); // 缩小字体
|
||||||
|
guiGraphics.drawString(font, text, (int)(textX / scale), (int)(textY / scale), 0xFFFFFFFF, true);
|
||||||
|
guiGraphics.pose().popPose();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染样板管理终端的数量显示
|
||||||
|
* @param guiGraphics GUI图形上下文
|
||||||
|
* @param screen 屏幕对象
|
||||||
|
*/
|
||||||
|
public static void renderPatternAmounts(GuiGraphics guiGraphics, Object screen) {
|
||||||
|
int scrollLevel;
|
||||||
|
int visibleRows;
|
||||||
|
ArrayList<?> rowsList;
|
||||||
|
|
||||||
|
if (screen instanceof PatternAccessTermScreenAccessor aeAccessor) {
|
||||||
|
var scrollbar = aeAccessor.getScrollbar();
|
||||||
|
if (scrollbar == null) return;
|
||||||
|
scrollLevel = scrollbar.getCurrentScroll();
|
||||||
|
visibleRows = aeAccessor.getVisibleRows();
|
||||||
|
if (visibleRows <= 0) return;
|
||||||
|
rowsList = aeAccessor.getRows();
|
||||||
|
if (rowsList == null || rowsList.isEmpty()) return;
|
||||||
|
} else if (screen instanceof GuiExPatternTerminalAccessor exAccessor) {
|
||||||
|
var scrollbar = exAccessor.getScrollbar();
|
||||||
|
if (scrollbar == null) return;
|
||||||
|
scrollLevel = scrollbar.getCurrentScroll();
|
||||||
|
visibleRows = exAccessor.getVisibleRows();
|
||||||
|
if (visibleRows <= 0) return;
|
||||||
|
rowsList = exAccessor.getRows();
|
||||||
|
if (rowsList == null || rowsList.isEmpty()) return;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 判断是否为ExtendedAE终端
|
||||||
|
boolean isExtendedAE = screen instanceof GuiExPatternTerminalAccessor;
|
||||||
|
|
||||||
|
// 根据终端类型使用不同的常量(与 AE2/ExtendedAE 源码保持一致)
|
||||||
|
final int SLOT_SIZE = 18; // ROW_HEIGHT == 18, SLOT_SIZE == ROW_HEIGHT
|
||||||
|
final int GUI_PADDING_X = isExtendedAE ? 22 : 8; // ExtendedAE使用22,AE2使用8
|
||||||
|
final int SLOT_Y_OFFSET = isExtendedAE ? 34 : 0; // ExtendedAE需要额外的Y偏移
|
||||||
|
|
||||||
|
var font = Minecraft.getInstance().font;
|
||||||
|
|
||||||
|
for (int i = 0; i < visibleRows; ++i) {
|
||||||
|
int rowIdx = scrollLevel + i;
|
||||||
|
if (rowIdx < 0 || rowIdx >= rowsList.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object row = rowsList.get(rowIdx);
|
||||||
|
if (row instanceof PatternAccessTermScreenSlotsRowAccessor slotsRow) {
|
||||||
|
var container = slotsRow.getContainer();
|
||||||
|
var inventory = container.getInventory();
|
||||||
|
drawRowAmounts(guiGraphics, font, inventory, slotsRow.getOffset(), slotsRow.getSlots(), i, SLOT_SIZE, GUI_PADDING_X, SLOT_Y_OFFSET);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row instanceof GuiExPatternTerminalSlotsRowAccessor exSlotsRow) {
|
||||||
|
var container = exSlotsRow.getContainer();
|
||||||
|
var inventory = container.getInventory();
|
||||||
|
drawRowAmounts(guiGraphics, font, inventory, exSlotsRow.getOffset(), exSlotsRow.getSlots(), i, SLOT_SIZE, GUI_PADDING_X, SLOT_Y_OFFSET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void drawRowAmounts(
|
||||||
|
GuiGraphics guiGraphics,
|
||||||
|
Font font,
|
||||||
|
AppEngInternalInventory inventory,
|
||||||
|
int offset,
|
||||||
|
int slots,
|
||||||
|
int visibleRowIndex,
|
||||||
|
int slotSize,
|
||||||
|
int guiPaddingX,
|
||||||
|
int slotYOffset
|
||||||
|
) {
|
||||||
|
for (int col = 0; col < slots; col++) {
|
||||||
|
int index = offset + col;
|
||||||
|
var pattern = inventory.getStackInSlot(index);
|
||||||
|
if (pattern == null || pattern.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String amountText = getPatternOutputText(pattern);
|
||||||
|
if (amountText.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int slotX = col * slotSize + guiPaddingX;
|
||||||
|
int slotY = (visibleRowIndex + 1) * slotSize + slotYOffset;
|
||||||
|
drawAmountText(guiGraphics, font, amountText, slotX, slotY, 0.6f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/main/java/com/extendedae_plus/util/NumberFormatUtil.java
Normal file
62
src/main/java/com/extendedae_plus/util/NumberFormatUtil.java
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
package com.extendedae_plus.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数字格式化工具类,提供大数字和小数的格式化功能
|
||||||
|
*/
|
||||||
|
public class NumberFormatUtil {
|
||||||
|
private NumberFormatUtil() {throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化数字,将大数字转换为k、m等格式
|
||||||
|
* 支持小数显示,小数点后为0则不显示0
|
||||||
|
* @param number 要格式化的数字
|
||||||
|
* @return 格式化后的字符串
|
||||||
|
*/
|
||||||
|
public static String formatNumber(long number) {
|
||||||
|
if (number < 1000) {
|
||||||
|
return String.valueOf(number);
|
||||||
|
} else if (number < 1000000) {
|
||||||
|
double value = number / 1000.0;
|
||||||
|
return formatDecimal(value, "k");
|
||||||
|
} else {
|
||||||
|
double value = number / 1000000.0;
|
||||||
|
return formatDecimal(value, "m");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化带小数的数字,支持流体等需要显示小数的场景
|
||||||
|
* @param value 小数值
|
||||||
|
* @return 格式化后的字符串
|
||||||
|
*/
|
||||||
|
public static String formatNumberWithDecimal(double value) {
|
||||||
|
if (value < 1000) {
|
||||||
|
if (value == (long) value) {
|
||||||
|
return String.valueOf((long) value);
|
||||||
|
} else {
|
||||||
|
return String.format("%.1f", value).replaceAll("\\.0$", "");
|
||||||
|
}
|
||||||
|
} else if (value < 1000000) {
|
||||||
|
return formatDecimal(value / 1000.0, "k");
|
||||||
|
} else {
|
||||||
|
return formatDecimal(value / 1000000.0, "m");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化小数,如果小数点后为0则不显示0
|
||||||
|
* @param value 小数值
|
||||||
|
* @param suffix 后缀(k、m、g等)
|
||||||
|
* @return 格式化后的字符串
|
||||||
|
*/
|
||||||
|
private static String formatDecimal(double value, String suffix) {
|
||||||
|
// 对于接近整数的值,使用整数显示
|
||||||
|
if (Math.abs(value - Math.round(value)) < 0.001) {
|
||||||
|
return String.valueOf(Math.round(value)) + suffix;
|
||||||
|
} else {
|
||||||
|
// 修复重复后缀问题:先格式化数字,再添加后缀
|
||||||
|
String formatted = String.format("%.1f", value).replaceAll("\\.0$", "");
|
||||||
|
return formatted + suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"accessor.AbstractContainerScreenAccessor",
|
"accessor.AbstractContainerScreenAccessor",
|
||||||
"accessor.ScreenAccessor",
|
"accessor.ScreenAccessor",
|
||||||
"accessor.ScreenInvoker",
|
"accessor.ScreenInvoker",
|
||||||
|
"ae2.PatternAccessTermScreenMixin",
|
||||||
"ae2.PatternEncodingTermScreenMixin",
|
"ae2.PatternEncodingTermScreenMixin",
|
||||||
"ae2.PatternProviderScreenMixin",
|
"ae2.PatternProviderScreenMixin",
|
||||||
"ae2.QuartzCuttingKnifeItemMixin",
|
"ae2.QuartzCuttingKnifeItemMixin",
|
||||||
|
|
@ -15,9 +16,13 @@
|
||||||
"ae2.accessor.AEBaseScreenAccessor",
|
"ae2.accessor.AEBaseScreenAccessor",
|
||||||
"ae2.accessor.AEBaseScreenInvoker",
|
"ae2.accessor.AEBaseScreenInvoker",
|
||||||
"ae2.accessor.MEStorageScreenAccessor",
|
"ae2.accessor.MEStorageScreenAccessor",
|
||||||
|
"ae2.accessor.PatternAccessTermScreenAccessor",
|
||||||
|
"ae2.accessor.PatternAccessTermScreenSlotsRowAccessor",
|
||||||
"extendedae.GuiExPatternProviderMixin",
|
"extendedae.GuiExPatternProviderMixin",
|
||||||
"extendedae.GuiExPatternTerminalMixin",
|
"extendedae.GuiExPatternTerminalMixin",
|
||||||
"extendedae.HighlightButtonMixin",
|
"extendedae.HighlightButtonMixin",
|
||||||
|
"extendedae.accessor.GuiExPatternTerminalAccessor",
|
||||||
|
"extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor",
|
||||||
"jei.EncodePatternTransferHandlerMixin"
|
"jei.EncodePatternTransferHandlerMixin"
|
||||||
],
|
],
|
||||||
"mixins": [
|
"mixins": [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user