众所周知,血魔法的炼金术桌与狱火熔炉的配方又多又杂,其自动化势在必行,但是某些配方含有重复物品,例如这个:

NBT处理实例之可堆叠物品分离-第1张图片

倘若正常发配则势必导致堆叠,何解?


其实站内早有解决方案,使用AE2的名称压印可以较为优雅地解决问题,但是此法在合成需求量庞大时压印器的工作效率问题就不容忽视了。因此如果你的整合包中碰巧有集成动力,那么也许可以考虑一些更高效的方案。


注意:

1. 对于此问题的特解,来自琅然的 倒序法 更加简单且优雅,这是一篇关于NBT提取的实例教程

2. 如果你有条件使用 NBT提取器,那么本教程的绝大部分步骤会变得更加简单

3. 此教程需要你了解集成动力基本的使用方式,否则你可能会难以理解下面的内容...


最简单的方案

首先说最简单的,狱火熔炉内部有四个材料格,接受侧面输入,于是我们刚好可以在它的四个侧面安排四个物品输出口,分别指向四个材料格:

NBT处理实例之可堆叠物品分离-第2张图片

上图展示了其中一个物品输出口的GUI,变量卡的值为0,代表其指向狱火熔炉内部的0号格子,其他三个输出口则分别为1,2,3

对于炼金术桌来说,其侧面可与内部的0-5号总共6个材料格交互,而炼金术桌占地两格,因此刚好有6个侧面,于是我们如法炮制即可:

NBT处理实例之可堆叠物品分离-第3张图片

简单粗暴,但有效。


不过也许你会想,万一炼金术桌只占地一格又该如何是好?这物品输出口贼难做啊能不能省一省(说的就是你DJ2)...

我也不太喜欢这个方案,它既不通用又不精巧,集成动力是一个强大的模组,它能做到的肯定不止这些。




更优雅的方案?

使用物品容器读取器读取炼金术桌,打开其GUI,你会发现物品总数槽数装有物品的槽数三个值:

NBT处理实例之可堆叠物品分离-第4张图片

槽数为6意味着这个指向炼金术桌侧面的物品容器读取器可以读取6个槽的内容,它们代表炼金术桌内部的6个材料格,其编号分别为0,1,2,3,4,5。

现在关注装有物品的槽数这一项,值为0意味着这个炼金术桌内所有的6个材料槽都为空,此时我们应该向0号材料槽放入物品;假如其中一个槽放入了物品,那么这个值就会变为1,此时我们应该向1号材料槽放入物品;如果有n个槽放入了物品,这个值就会变为n,此时我们应该向n号槽放入物品。

不难发现,装有物品的槽数这一项的值就是我们接下来放入物品的目标槽编号,于是我们只需用变量卡取出这个值,然后把它作为物品输出口中「输出物品到指定位置」的参数即可:

NBT处理实例之可堆叠物品分离-第5张图片

注意:需要点击「输出物品到指定位置」项右侧的加号,并把「物品传输速率」设定为1:

NBT处理实例之可堆叠物品分离-第6张图片

以上,显然是一个更好的方案,但是这个方案仍然有两个问题:


1、对于狱火熔炉来说,不能如法炮制,如果你使用物品容器读取器读取它会发现,侧面可交互的格子有6个而不是4个,因为产物格和魂石格也被包括在内,仍然按上面的做法是不行的,其解决方案会在本教程末尾附上,如果你在看完教程后认为上面这个方案已经能够解决你的所有问题,那么你可以直接参考那个解决方案。


2、考虑下面这个配方:

NBT处理实例之可堆叠物品分离-第7张图片

这里的水桶在使用过后会留下空桶,想象在炼金术桌中,1号格和2号格分别留有一个空桶,其他的0,3,4,5号格为空,此时装有物品的槽数为2,但是2号格中有一个空桶,于是机器会卡住。

你意识到了这一点,于是做了一个空桶的导出装置,不断从材料格中取出空桶,于是两个空桶依次被取出,当取出1号槽的空桶时,装有物品的槽数为1,1号槽被放入物品,紧接着2号槽的空桶被取出,装有物品的槽数1但是1号格已经被放入物品,于是机器再次卡住。

出现这种情况的原因在于,装有物品的槽数只在严格按顺序放入物品的情况下能够表示第一个空槽的序号,但凡出现第一个非空的槽不是0号槽的情况,就会导致机器卡住。于是乎,我们需要实现的是真正意义上的读取第一个空槽的序号


最终的方案

我们需要做的是:读取每个原料槽的信息 → 找出第一个空的原料槽 → 往这个原料槽中放入一个原料。

第一步,读取每个原料槽的信息,这很简单,物品容器读取器可以将它能读取的格子信息汇成一个列表:

NBT处理实例之可堆叠物品分离-第8张图片

来到第二步,找出第一个空的原料槽,首先想到的显然是实现一个在列表中寻找满足条件的第一个元素的下标的方法,但是遗憾的是,集成动力并未提供这样的运算符。不过不要忘了,当由读取器定制好的信息不能满足你的需求时,我们还有方块读取器,它能够读取方块的NBT信息,我们可以在NBT中找到各种我们想要的东西。下面是一个炼金术桌的NBT信息与它的GUI内容(槽位下的数字代表槽位编号):

NBT处理实例之可堆叠物品分离-第9张图片

不难发现,在「Items」键下是一个列表,存有每个非空格子中的物品信息,每个物品下的「Slot」键的值为这个物品所在的槽位编号。

那么我们只需要取出每个物品下方「Slot」键的值,汇成一个列表,在上面的例子中是[0,1,2,4,5,6],记为A,另写一个列表B = [0,1,2,3,4,5,114514],只需找出B中所有在A中没出现的数字,汇成列表C(在这个例子中,C = [3,114514])那么列表C的第一个元素就是我们要找的第一个空的原料槽的序号,当原料槽均非空时,C的第一个元素会是114514,这个序号下没有对应的原料槽,于是不会输入原料


思路有了,接下来就是编程时间。


首先在方块读取器中取出炼金术桌的NBT,然后写一个“Items”的字符串常量,把它和NBT代入NBT.list_tag(NBT数据:NBT列表)运算符中即可取出「Items」键下的物品列表:

NBT处理实例之可堆叠物品分离-第10张图片

这个列表的每个值都是一个NBT,我们要提取这些NBT中「Slot」键的值,首先需要自定义一个输入NBT→输出NBT标签中“Slot”键下方的整型值的运算符。以下是具体步骤(变量命名纯属个人习惯,旨在方便表达,你可以使用你喜欢的任何命名方式):

1.将上图中的物品NBT列表命名为「NBT_List

2.提取NBT.integer(NBT数据:整型)运算符,并命名为「NBT2int

NBT处理实例之可堆叠物品分离-第11张图片

3.将「NBT2int」代入flip(翻转)运算符中,取出新运算符并命名为「NBT2int_flipped

4.写一个内容为"Slot"的字符串常量,并命名为「"Slot"

5.将「NBT2int_flipped」与「"Slot"」代入apply(一元执行)运算符中,取出新运算符并命名为「Pick_Slot」,这便是我们要的自定义运算符。

然后,将「Pick_Slot」与「NBT_List」代入map(遍历映射)运算符中,取出返回的列表并命名为「Slot_List」, 我们便得到了由各物品的「Slot」键值组成的列表。

NBT处理实例之可堆叠物品分离-第12张图片

接下来,写一个整型列表,其内容为[0,1,2,3,4,5,114514],并命名为「Num_List」,然后需要从中取出没有出现在「Slot_List」中的数字,具体步骤如下:

1.取出contains(列表包含)运算符,并命名为「contains

2.将「contains」与「Slot_List」代入apply(一元执行)运算符中,取出新运算符并命名为「in_Slot_List

3.取出!(逻辑非)运算符,并命名为「Not

4.将「in_Slot_List」与「Not」依次放入 .(管道) 运算符内,取出新运算符并命名为「Not_in_Slot_List

5.将「Not_in_Slot_List」与「Num_List」放入filter(过滤)运算符中,取出返回的列表并命名为「Empty_Slots」,至此,我们取出了炼金术桌中所有空的材料槽的序号:

NBT处理实例之可堆叠物品分离-第13张图片

最后,将「Empty_Slots」代入head(首)运算符中,取出返回的变量并命名为「First_Empty_Slot」,我们便完成了我们的使命。

将「First_Empty_Slot」放入物品输出口的输出物品到指定位置选项中,并点击这一行的加号,将物品传输速率改为1,并且不要忘记不断导出水桶,再配合上AE2的基础物流和材料发配,我们便完成了一个精巧且高效的炼金术桌自动化(对于狱火熔炉则是完全一样的步骤,依样画葫芦即可)。


最后

集成动力是十分强大的模组,如果搭配AE2能够完成非常多复杂的任务,但奈何其知名度低教程又少,而且上手门槛很高。上面的步骤看似繁琐且复杂,但理解了之后其实是一套很流畅的逻辑,总之一句话,你赶紧和AE2联动啊啊啊


附:第二种方案的狱火熔炉版本

1.在物品容器读取器中取出狱火熔炉内的物品列表,并命名为「Items

2.写一个值为0的整型常量和一个值为4的整型常量,分别命名为「0」和「4

3.将「Items」、「0」和「4」依次放入slice(切片)运算符中,取出返回的列表并命名为「Inputs

4.取出o(非空)运算符,并命名为「Not_Empty」

5.将「Items」与「Not_Empty」依次放入count_p(条件计数)运算符中,取出返回的值并命名为「Num_of_Full_Inputs

6.将「Num_of_Full_Inputs」放入物品输出口的输出物品到指定位置选项中,并点击这一行的加号,将物品传输速率改为1即可(不要忘记不断导出水桶)。