滚轮搬运(WheelTweak)

基本信息

属性 值
配置字段 MTConfig.WheelTweak
类型 boolean
默认值 true(MTConfig.java 字段初始值)
实现类 yalter.mousetweaks.handlers.WheelHandler(静态方法 handleWheelTweak)
触发输入 鼠标滚轮(Mouse.getDWheel(),非 LWJGL3 下每格 = 120)
与 NEI 互斥 是,见下方「NEI 交互」

功能

在任意受支持的容器界面里,把鼠标悬停在某堆物品上滚动滚轮,即可批量在 光标栏位与「另一个背包」之间搬运同类型物品。

前置守卫(逐层返回)

顺序 条件 不满足时
1 wheel != 0 且 selectedSlot != null 直接返回
2 slotCount > Constants.INVENTORY_SIZE(36) 直接返回——栏位数不超过玩家背包大小时不存在「另一个背包」
3 originalStack != null 直接返回(空栏位不触发)
4 鼠标上的物品与原栏位物品的关系正确 直接返回(详见下)

第 4 条的判定(WheelHandler.java:31-35)随栏位类型分叉:

栏位类型 允许继续的条件
合成产物栏位 areStacksCompatible(originalStack, stackOnMouse) 为 false 才对(鼠标上的东西与产物不同类才处理)
普通栏位 areStacksCompatible(originalStack, stackOnMouse) 为 true 才对(鼠标上的东西与原栏位同类才处理)

只在「另一半」区间里搜索

int countUntil = slotCount - 36;                    // 玩家背包起始下标
if (getSlotNumber(selectedSlot) < countUntil) {
    slotCounter = countUntil;                       // 光标在上半区 → 扫玩家背包
    countUntil   = slotCount;
}                                                   // 否则 → 扫 [0, slotCount-36)

搜索区间永远是光标所在区域的对侧:

  • 光标在机器/箱子区(下标 < slotCount - 36)→ 只在玩家背包 [slotCount-36, slotCount) 里找;
  • 光标在玩家背包区 → 只在机器/箱子区 [0, slotCount-36) 里找。

因此滚轮不会在同一区域内来回搬运,也不会把物品丢进光标所在的栏位。

目标栏位选取

方向 遇到空栏位 遇到同类型且未满的栏位 遇到同类型且已满的栏位 遇到不同类型
推入(push) 记为候选(需 sl.isItemValid(originalStack) 且非产物栏位),但继续找更好的 立即选中并中断扫描 继续扫描 继续扫描
拉出(pull) 永不选中 立即选中并中断扫描 继续扫描 继续扫描

⚠️ 源码不对称点:正序扫描分支判定空栏位时会额外检查 !context.isCraftingOutputSlot(currentScreen, sl), 倒序扫描分支没有这个检查(WheelHandler.java:75)。选 搜索顺序 = 1(默认,倒序) 时,理论上可能把一个产物栏位当成空目标栏位。

若整个区间扫描完 applicableSlot == null,则直接跳出循环不做任何操作。

点击序列

numItemsToMove = 本次滚轮刻度换算出的物品数(见 滚轮物品数量缩放)。 推入时先 numItemsToMove = Math.min(numItemsToMove, count(光标栏位)); 拉出时按来源栏位的实际数量和目标剩余容量逐步递减。

推入(从光标栏位 → 目标栏位)

情形 点击序列(L=左键 0,R=右键 1,均不按 Shift) 剩余量处理
目标剩余容量 ≤ 待移数量 L(来源) → L(目标) → L(来源) n -= 目标剩余容量,进入下一轮
目标剩余容量 > 待移数量,来源数量 ≤ n L(来源) → L(目标) → L(来源) n = 0,结束
目标剩余容量 > 待移数量,来源数量 > n L(来源) → R(目标) × n → L(来源) n = 0,结束

「三连击」L(来源) → L(目标) → L(来源) 的效果是:拿起来源整堆 → 放掉目标原有那堆(腾位并拾起) → 把来源整堆补进目标,实现整堆合并。「R × n」则是逐个右键放置,用于只取一部分。

拉出(从目标栏位 → 光标栏位)

情形 点击序列 剩余量处理
maxStack(目标) - count(目标) <= 0 无(break 跳出) 结束
光标栏位剩余容量 ≤ n L(来源) → L(光标) → L(来源) n -= 剩余容量,进入下一轮
剩余容量 > n,来源数量 ≤ n L(来源) → L(光标) → L(来源) n -= 来源数量,继续找下一个
剩余容量 > n,来源数量 > n L(来源) → R(光标) × n → L(来源) n = 0,结束

外层是 do { ... } while (numItemsToMove != 0);——一次滚轮输入可以跨多个栏位连锁搬运, 直到数量耗尽或找不到可用栏位。

合成产物栏位的特殊处理

光标停在产物栏位时只支持推入(拉入会复制物品,故不实现):

  1. 连点 numItemsToMove 次 L(光标栏位)——每点一次取出一个产物;
  2. 若找到了目标栏位且操作开始前鼠标是空的(mouseWasEmpty = stackOnMouse == null), 再点一次 L(目标栏位) 把取出的产物放进去;
  3. break 结束,不再进入多轮循环。

因此在产物栏上滚轮,取出的产物只有第 1 个会被送走,其余停留在鼠标上。

NEI 交互

Runtime.isMouseWheelTransferActive():

if (Loader.isModLoaded("NotEnoughItems")) {
    return MTConfig.WheelTweak && !NEIClientConfig.isMouseScrollTransferEnabled();
}
return MTConfig.WheelTweak;

即:若 NEI 已安装且 NEI 自己的「鼠标滚轮转移」也开着,Mouse Tweaks 整个滚轮功能 就让位给 NEI(WheelTweak 配成 true 也不生效),避免两者同时处理同一滚轮事件。

开关层级

层级 判定 位置
全局 MTConfig.WheelTweak Runtime#isMouseWheelTransferActive
NEI 互斥 !NEIClientConfig.isMouseScrollTransferEnabled() 同上(仅 NEI 存在时)
单个容器 context.isWheelDisabledForCurrentContainer() Runtime#onUpdateInGui
单个 mod 容器 IMTModGuiContainer#isWheelTweakDisabled() ModCompatibility

滑轮增量在打开新界面时会被主动清空:ContainerContext.refresh 中若滚轮功能处于活动状态 且未被该容器禁用,会调用一次 Mouse.getDWheel() 把累积的增量丢掉,避免切界面瞬间误滚。

相关条目