一、使用 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/
|
||||
out/
|
||||
classes/
|
||||
source/
|
||||
|
||||
# Eclipse
|
||||
*.tmp
|
||||
|
|
|
|||
|
|
@ -94,12 +94,9 @@ dependencies {
|
|||
modCompileOnly "curse.maven:just-enough-characters-250702:6680042"
|
||||
}
|
||||
|
||||
// 忽略所有过时警告
|
||||
gradle.projectsEvaluated {
|
||||
tasks.withType(JavaCompile).tap {
|
||||
configureEach {
|
||||
options.compilerArgs << "-Xlint:-deprecation"
|
||||
}
|
||||
allprojects {
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << "-Xlint:-deprecation"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
||||
/**
|
||||
* 给 AE2 的 PatternEncodingTermMenu 增加一个通用动作持有者,实现接收 EPP 的 CGenericPacket 动作。
|
||||
* 注册动作 "upload_to_matrix":仅上传“合成图样”到 ExtendedAE 装配矩阵。
|
||||
|
|
@ -53,8 +52,7 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
|
|||
eap$scheduleUploadWithRetry(sp, menu, attemptsLeft - 1);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Error uploading pattern to matrix", t);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -87,8 +85,10 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
|
|||
return; // 仅服务器执行
|
||||
}
|
||||
var menu = (PatternEncodingTermMenu) (Object) this;
|
||||
if (menu.getMode() != EncodingMode.CRAFTING) {
|
||||
return; // 只处理合成样板
|
||||
if (menu.getMode() != EncodingMode.CRAFTING
|
||||
&& menu.getMode() != EncodingMode.SMITHING_TABLE
|
||||
&& menu.getMode() != EncodingMode.STONECUTTING) {
|
||||
return; // 只处理合成/锻造台/切石机样板
|
||||
}
|
||||
if (this.encodedPatternSlot == null) {
|
||||
return;
|
||||
|
|
@ -107,8 +107,7 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
|
|||
} catch (Throwable ignored) {
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Error uploading pattern to matrix", t);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,65 +3,111 @@ package com.extendedae_plus.mixin.extendedae;
|
|||
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;
|
||||
import appeng.menu.AEBaseMenu;
|
||||
import com.extendedae_plus.util.GuiUtil;
|
||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
||||
import com.glodblock.github.extendedae.container.ContainerExPatternTerminal;
|
||||
import com.glodblock.github.extendedae.network.EPPNetworkHandler;
|
||||
import com.glodblock.github.glodium.network.packet.CGenericPacket;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.Tooltip;
|
||||
import net.minecraft.client.renderer.Rect2i;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
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.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
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)
|
||||
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
|
||||
private static final String UPLOAD_SUCCESS_MESSAGE = "✅ ExtendedAE Plus: 样板快速上传成功!";
|
||||
|
||||
@Unique
|
||||
private static final String UPLOAD_FAILED_MESSAGE = "❌ ExtendedAE Plus: 样板上传失败,请检查供应器状态";
|
||||
|
||||
@Unique
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@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
|
||||
*/
|
||||
@Unique
|
||||
public long getCurrentlyChoicePatternProvider() {
|
||||
return currentlychooicepatterprovider;
|
||||
return eap$currentlyChoicePatterProvider;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置当前选择的样板供应器ID
|
||||
*/
|
||||
@Unique
|
||||
public void setCurrentlyChoicePatternProvider(long id) {
|
||||
this.currentlychooicepatterprovider = id;
|
||||
this.eap$currentlyChoicePatterProvider = id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 拦截鼠标点击事件,实现Shift+左键快速上传样板功能
|
||||
*/
|
||||
|
|
@ -74,22 +120,22 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
|||
if (hoveredSlot != null && hoveredSlot.container == this.minecraft.player.getInventory()) {
|
||||
// 点击的是玩家背包槽位
|
||||
ItemStack clickedItem = hoveredSlot.getItem();
|
||||
|
||||
|
||||
// 检查是否是有效的编码样板
|
||||
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);
|
||||
} else {
|
||||
// 显示提示消息:请先选择一个样板供应器
|
||||
if (this.minecraft.player != null) {
|
||||
this.minecraft.player.displayClientMessage(
|
||||
Component.literal("ExtendedAE Plus: 请先选择一个样板供应器(点击GroupHeader旁的按钮)"),
|
||||
true
|
||||
Component.literal("ExtendedAE Plus: 请先选择一个样板供应器(点击GroupHeader旁的按钮)"),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -97,44 +143,59 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 快速上传样板到当前选择的供应器
|
||||
*/
|
||||
@Unique
|
||||
private void quickUploadPattern(int playerSlotIndex) {
|
||||
private void eap$quickUploadPattern(int playerSlotIndex) {
|
||||
if (this.minecraft.player != null) {
|
||||
// 获取要上传的物品
|
||||
ItemStack itemToUpload = this.minecraft.player.getInventory().getItem(playerSlotIndex);
|
||||
|
||||
|
||||
if (!itemToUpload.isEmpty() && PatternDetailsHelper.isEncodedPattern(itemToUpload)) {
|
||||
// 通过 ExtendedAE 内置网络系统发送通用动作到服务端
|
||||
// 动作: "upload",参数: 槽位索引(int)、供应器ID(long)
|
||||
EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("upload", playerSlotIndex, currentlychooicepatterprovider));
|
||||
|
||||
// 通过反射调用 ExtendedAE 的网络发送(软依赖)
|
||||
try {
|
||||
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 {
|
||||
this.minecraft.player.displayClientMessage(
|
||||
Component.literal("❌ ExtendedAE Plus: 无效的样板物品"),
|
||||
true
|
||||
Component.literal("❌ ExtendedAE Plus: 无效的样板物品"),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 重置当前选择的样板供应器ID
|
||||
*/
|
||||
@Unique
|
||||
public void resetCurrentlyChoicePatternProvider() {
|
||||
this.currentlychooicepatterprovider = -1;
|
||||
this.eap$currentlyChoicePatterProvider = -1;
|
||||
}
|
||||
|
||||
@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.showSlots = !this.showSlots; // 开关状态
|
||||
|
||||
this.eap$toggleSlotsButton = new IconButton((b) -> {
|
||||
this.eap$showSlots = !this.eap$showSlots; // 开关状态
|
||||
|
||||
// 通过反射调用refreshList方法 - 先尝试当前类,失败后尝试父类
|
||||
try {
|
||||
java.lang.reflect.Method refreshMethod = null;
|
||||
|
|
@ -149,41 +210,40 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
|||
throw e2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
refreshMethod.setAccessible(true);
|
||||
refreshMethod.invoke(this);
|
||||
} catch (Exception e) {
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}) {
|
||||
@Override
|
||||
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)
|
||||
private void onRefreshListStart(CallbackInfo ci) {
|
||||
|
||||
// 更新按钮图标
|
||||
if (this.toggleSlotsButton != null) {
|
||||
this.toggleSlotsButton.setTooltip(Tooltip.create(Component.translatable(
|
||||
this.showSlots ? "gui.expatternprovider.hide_slots" : "gui.expatternprovider.show_slots"
|
||||
if (this.eap$toggleSlotsButton != null) {
|
||||
this.eap$toggleSlotsButton.setTooltip(Tooltip.create(Component.translatable(
|
||||
this.eap$showSlots ? "gui.expatternprovider.hide_slots" : "gui.expatternprovider.show_slots"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "refreshList", at = @At("TAIL"), remap = false)
|
||||
private void onRefreshListEnd(CallbackInfo ci) {
|
||||
|
||||
|
||||
// 在refreshList结束后,根据showSlots状态过滤SlotsRow
|
||||
if (!this.showSlots) {
|
||||
if (!this.eap$showSlots) {
|
||||
try {
|
||||
// 通过反射访问rows字段 - 先尝试当前类,失败后尝试父类
|
||||
java.lang.reflect.Field rowsField = null;
|
||||
|
|
@ -200,7 +260,7 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
|||
}
|
||||
rowsField.setAccessible(true);
|
||||
java.util.ArrayList<?> rows = (java.util.ArrayList<?>) rowsField.get(this);
|
||||
|
||||
|
||||
// 通过反射访问highlightBtns字段
|
||||
java.lang.reflect.Field highlightBtnsField = null;
|
||||
try {
|
||||
|
|
@ -217,22 +277,22 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
|||
highlightBtnsField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
java.util.HashMap<Integer, Object> highlightBtns = (java.util.HashMap<Integer, Object>) highlightBtnsField.get(this);
|
||||
|
||||
|
||||
// 创建新的索引映射
|
||||
java.util.HashMap<Integer, Object> newHighlightBtns = new java.util.HashMap<>();
|
||||
int newIndex = 0;
|
||||
|
||||
|
||||
// 移除所有SlotsRow,只保留GroupHeaderRow,同时重新映射高亮按钮索引
|
||||
for (int i = 0; i < rows.size(); i++) {
|
||||
Object row = rows.get(i);
|
||||
String className = row.getClass().getSimpleName();
|
||||
|
||||
|
||||
if (className.equals("GroupHeaderRow")) {
|
||||
// 保留GroupHeaderRow,并重新映射对应的高亮按钮
|
||||
@SuppressWarnings("unchecked")
|
||||
java.util.ArrayList<Object> typedRows = (java.util.ArrayList<Object>) rows;
|
||||
typedRows.set(newIndex, row);
|
||||
|
||||
|
||||
// 查找原来在这个位置的高亮按钮
|
||||
// 原始代码中,高亮按钮的索引是在添加GroupHeaderRow之后、添加第一个SlotsRow之前设置的
|
||||
// 所以按钮的索引指向的是第一个SlotsRow的位置
|
||||
|
|
@ -241,25 +301,25 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
|||
Object button = highlightBtns.get(i + 1);
|
||||
newHighlightBtns.put(newIndex, button);
|
||||
}
|
||||
|
||||
|
||||
newIndex++;
|
||||
} else if (className.equals("SlotsRow")) {
|
||||
// 不保留SlotsRow,也不增加newIndex
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 移除多余的行
|
||||
while (rows.size() > newIndex) {
|
||||
rows.remove(rows.size() - 1);
|
||||
}
|
||||
|
||||
|
||||
// 更新highlightBtns
|
||||
highlightBtns.clear();
|
||||
highlightBtns.putAll(newHighlightBtns);
|
||||
|
||||
|
||||
// 强制刷新滚动条
|
||||
try {
|
||||
java.lang.reflect.Method resetScrollbarMethod = null;
|
||||
Method resetScrollbarMethod = null;
|
||||
try {
|
||||
// 先尝试在当前类中查找
|
||||
resetScrollbarMethod = this.getClass().getDeclaredMethod("resetScrollbar");
|
||||
|
|
@ -271,14 +331,80 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<ContainerEx
|
|||
throw e2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resetScrollbarMethod.setAccessible(true);
|
||||
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.gui.GuiExPatternTerminal;
|
||||
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.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Mixin(value = HighlightButton.class, priority = 1000)
|
||||
public abstract class HighlightButtonMixin {
|
||||
@Shadow(remap = false)
|
||||
private static void highlight(Button btn) {}
|
||||
|
||||
@Shadow(remap = false)
|
||||
private static void highlight(Button btn) {}
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("ExtendedAEPlus");
|
||||
|
||||
@Inject(method = "highlight", at = @At("TAIL"), remap = false)
|
||||
private static void onHighlight(Button btn, CallbackInfo ci) {
|
||||
if (btn instanceof HighlightButton hb) {
|
||||
// 获取当前打开的GUI屏幕
|
||||
var minecraft = net.minecraft.client.Minecraft.getInstance();
|
||||
if (minecraft.screen instanceof GuiExPatternTerminal<?> terminal) {
|
||||
// 通过反射获取HighlightButton的serverId信息
|
||||
try {
|
||||
// 获取HighlightButton的pos字段,用于标识对应的样板供应器
|
||||
var posField = HighlightButton.class.getDeclaredField("pos");
|
||||
posField.setAccessible(true);
|
||||
var pos = posField.get(hb);
|
||||
|
||||
if (pos != null) {
|
||||
// 通过反射访问infoMap字段
|
||||
var infoMapField = GuiExPatternTerminal.class.getDeclaredField("infoMap");
|
||||
infoMapField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
var infoMap = (java.util.Map<Long, Object>) infoMapField.get(terminal);
|
||||
|
||||
// 查找对应的样板供应器ID
|
||||
for (var entry : infoMap.entrySet()) {
|
||||
var info = entry.getValue();
|
||||
// 通过反射调用pos()方法
|
||||
var posMethod = info.getClass().getMethod("pos");
|
||||
var infoPos = posMethod.invoke(info);
|
||||
|
||||
if (pos.equals(infoPos)) {
|
||||
long serverId = entry.getKey();
|
||||
|
||||
// 通过反射调用setter方法
|
||||
try {
|
||||
var setMethod = terminal.getClass().getMethod("setCurrentlyChoicePatternProvider", long.class);
|
||||
setMethod.invoke(terminal, serverId);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Inject(method = "highlight", at = @At("TAIL"), remap = false)
|
||||
private static void onHighlight(Button btn, CallbackInfo ci) {
|
||||
if (btn instanceof HighlightButton hb) {
|
||||
var minecraft = net.minecraft.client.Minecraft.getInstance();
|
||||
if (minecraft.screen instanceof GuiExPatternTerminal<?> terminal) {
|
||||
try {
|
||||
var fPos = HighlightButton.class.getDeclaredField("pos");
|
||||
fPos.setAccessible(true);
|
||||
Object btnPos = fPos.get(hb);
|
||||
if (btnPos == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fFace = HighlightButton.class.getDeclaredField("face");
|
||||
fFace.setAccessible(true);
|
||||
Object btnFace = fFace.get(hb); // 允许为 null:方块形
|
||||
|
||||
var infoMapField = GuiExPatternTerminal.class.getDeclaredField("infoMap");
|
||||
infoMapField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
var infoMap = (java.util.Map<Long, Object>) infoMapField.get(terminal);
|
||||
|
||||
for (var entry : infoMap.entrySet()) {
|
||||
var info = entry.getValue();
|
||||
var mPos = info.getClass().getMethod("pos");
|
||||
mPos.setAccessible(true);
|
||||
Object infoPos = mPos.invoke(info);
|
||||
|
||||
var mFace = info.getClass().getMethod("face");
|
||||
mFace.setAccessible(true);
|
||||
Object infoFace = mFace.invoke(info); // 允许为 null:方块形
|
||||
|
||||
// 匹配规则:pos 必须相等;face 允许为 null,null 仅与 null 匹配
|
||||
boolean posEqual = Objects.equals(btnPos, infoPos);
|
||||
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);
|
||||
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.core.definitions.AEItems;
|
||||
import appeng.crafting.pattern.AECraftingPattern;
|
||||
import appeng.crafting.pattern.AESmithingTablePattern;
|
||||
import appeng.crafting.pattern.AEStonecuttingPattern;
|
||||
import appeng.helpers.patternprovider.PatternContainer;
|
||||
import appeng.menu.implementations.PatternAccessTermMenu;
|
||||
import appeng.menu.me.items.PatternEncodingTermMenu;
|
||||
|
|
@ -433,10 +435,12 @@ public class ExtendedAEPatternUploadUtil {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 仅允许“合成图样”
|
||||
// 仅允许“合成/锻造台/切石机图样”
|
||||
IPatternDetails details = PatternDetailsHelper.decodePattern(stack, player.level());
|
||||
if (!(details instanceof AECraftingPattern)) {
|
||||
sendMessage(player, "extendedae_plus.upload_to_matrix.fail_not_crafting");
|
||||
if (!(details instanceof AECraftingPattern
|
||||
|| details instanceof AESmithingTablePattern
|
||||
|| details instanceof AEStonecuttingPattern)) {
|
||||
sendMessage(player, "extendedae_plus.upload_to_matrix.fail");
|
||||
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.ScreenAccessor",
|
||||
"accessor.ScreenInvoker",
|
||||
"ae2.PatternAccessTermScreenMixin",
|
||||
"ae2.PatternEncodingTermScreenMixin",
|
||||
"ae2.PatternProviderScreenMixin",
|
||||
"ae2.QuartzCuttingKnifeItemMixin",
|
||||
|
|
@ -15,9 +16,13 @@
|
|||
"ae2.accessor.AEBaseScreenAccessor",
|
||||
"ae2.accessor.AEBaseScreenInvoker",
|
||||
"ae2.accessor.MEStorageScreenAccessor",
|
||||
"ae2.accessor.PatternAccessTermScreenAccessor",
|
||||
"ae2.accessor.PatternAccessTermScreenSlotsRowAccessor",
|
||||
"extendedae.GuiExPatternProviderMixin",
|
||||
"extendedae.GuiExPatternTerminalMixin",
|
||||
"extendedae.HighlightButtonMixin",
|
||||
"extendedae.accessor.GuiExPatternTerminalAccessor",
|
||||
"extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor",
|
||||
"jei.EncodePatternTransferHandlerMixin"
|
||||
],
|
||||
"mixins": [
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user