配方消歧

从 NEI 导入配方时,同一个槽位常常有多个候选物品(NEI 的一个槽位往往是 若干可选 ItemStack 的数组)。GuiRecipe 用一个 disamb 映射把这些候选存起来, 并用一个浮层让玩家挑一个代表。

基本信息

属性 值
实现类 me.towdium.jecalculation.gui.guis.GuiRecipe
核心字段 EnumMap<IO, Map<Integer, List<ILabel>>> disamb(IO → 槽位下标 → 候选标签)
触发条件 p.two.getLabels().size() > 1(该槽位的代价列表含多于一个标签)
浮层类 内部类 WDisamb(IO type, int idx)
数量增减 handleLabelScroll(WLabel w, int diff) 静态方法

候选是怎么生成的

GuiRecipe.extract(EnumMap<IO, List<Trio<ILabel, CostList, CostList>>> src, IO type, Class<?> context):

l = src.get(type);  若为 null → 返回空列表
for (i = 0; i < l.size(); i++):
    p = l.get(i)
    ret.add(p.one)                                  // ← 代表标签直接占位
    if (p.two.getLabels().size() > 1):              // ← 触发条件
        raw = p.two.getLabels()
        suggest = CONVERTER.guess(raw, context).one
        suggest.addAll(raw)                          // ← 建议在前,原始候选在后
        disamb.computeIfAbsent(type, ...).put(i, suggest)
return ret

候选列表的构成是 SUGGEST 的结果 + 原始候选。也就是说玩家总会先看到 「更宽松的建议标签」(例如把某个具体的机器零件建议成 oreDict), 若都不满意再看到 NEI 给的原始 ItemStack。这依赖 标签类型与合并 里 CONVERTER 的 SUGGEST / FALLBACK 两级优先级。

transfer(...) 每次调用开头都会 disamb.clear(),所以重新从 NEI 导入会丢弃上次的消歧记录。

浮层按钮

每个槽位旁有一排 6 个按钮(WAmount 内部类构造):

按钮 坐标 提示键 行为
# x+78, y-1 recipe.to_percent temp.getLabel().setPercent(true)
% 同上(同坐标,互斥显示) recipe.to_amount temp.getLabel().setPercent(false)
取物图标 x+21, y-24 recipe.pick 把 temp 挂到 JecaGui.getCurrent().hand,并把原槽位置空
确认 x+59, y-24 recipe.confirm set(temp.getLabel(), type, idx) —— 把输入框的值写回槽位
删除 x+78, y-24 recipe.delete set(ILabel.EMPTY, type, idx) —— 清空槽位
消歧 x+40, y-24 recipe.disamb 打开 WDisamb 浮层

# 与 % 两个按钮坐标完全相同(都在 x+78, y-1),靠 update() 切换可见性: 当前标签是绝对数量时显示 #(可切到百分比),是百分比时显示 %(可切回绝对数量)。

消歧按钮的禁用逻辑

Map<Integer, List<ILabel>> entry = GuiRecipe.this.disamb.get(type);
if (entry != null && entry.containsKey(idx)) {
    disamb.setListener(i -> GuiRecipe.this.setOverlay(new WDisamb(type, idx)));
} else disamb.setDisabled(true);

该槽位没有多个候选时,消歧按钮是灰的。

数量输入的校验

WAmount 里的 WTextField 监听器有一套手写校验:

try:
    amount = Long.parseLong(text.getText())
    acceptable = amount > 0
    if (!acceptable) amount = 1            // 非正数被改写为 1
catch NumberFormatException:
    acceptable = text.getText().isEmpty()
    amount = 1
text.setColor(acceptable ? COLOR_TEXT_WHITE : COLOR_TEXT_RED)
yes.setDisabled(!acceptable)
temp.setLabel(temp.getLabel().setAmount(amount))
输入 acceptable 颜色 确认按钮
正整数 true 白 可用
0 / 负数 false(量被改成 1) 红 禁用
非数字非空 false(量被改成 1) 红 禁用
空串 true(量被改成 1) 白 可用

即空输入是合法的(视为 1),但「非数字且非空」是红色非法输入。 注意非法输入并不会阻止数量被改成 1,只是把确认按钮禁掉。

数量增减

handleLabelScroll 是一个静态方法,按滚轮差值反复调用 increaseAmount() / decreaseAmount():

static boolean handleLabelScroll(WLabel w, int diff) {
    ILabel l = w.getLabel();
    for (int i = 0; i < Math.abs(diff); i++) l = diff > 0 ? l.increaseAmount() : l.decreaseAmount();
    w.setLabel(l, true);
    return true;
}

恒返回 true,即永远宣称「事件已处理」,滚轮不会继续冒泡给父控件。 增减步长由 Impl.increaseAmount() / decreaseAmount() 定义(对百分比标签会相应调整量级)。

保存前的重复检测

refresh() 在每次变化后重新校验当前配方能否保存:

try {
    Recipe r = toRecipe();
    boolean d = dest == null ? Controller.hasDuplicate(r) : Controller.hasDuplicate(r, dest.one, dest.two);
    save.setDisabled(d);
    if (dest != null) copy.setDisabled(d);
} catch (IllegalArgumentException e) {
    save.setDisabled(true);
    copy.setDisabled(true);
}

IllegalArgumentException 来自 Recipe 构造器——它会拒绝两种非法配方:

校验 条件
a input / output / catalyst 任一列表的最后一个元素是 ILabel.EMPTY
b input 或 output 中有列表全部元素都是 ILabel.EMPTY

toRecipe() 构造前会 trim(...) 掉尾部所有的 EMPTY(从后往前 remove), 所以第一个条件在正常路径下不会触发。

相关条目