「MC筛种杂谈2」结构生成的位置选择,箱子位置和战利品的决定
负一、宣群
筛种、源码解读交流群956255985,欢迎。
零、上期课后题解
一把金镐,在设置随机附魔(EnchantRandomly)时,得到效率5的概率
为什么说20附魔金苹果的古城箱子是不存在的
预测一个废弃传送门的箱子,有6个附魔金苹果的可能性
为什么桥类型的堡垒遗迹必出一个磁石
因为第一个奖池抽奖次数是固定的1,且只有一个奖项——磁石。
mojang在1.20更新中,为许多结构增加了锻造模板的战利品,而不影响原有的战利品,这是怎么实现的
在原有奖池后面添加了新奖池用以生成锻造模板,这并不影响之前的战利品对随机数生成器的使用,也就保持了原有的随机数序列,得到了一样的战利品。

一、结构生成的位置选择
大部分结构遵循区域(Region)生成。一个结构的设定(setting)通常有三个要素:间距(spacing),间隔(separation),盐(salt),散布(spread)。
spacing代表了区域的大小,每个区域内,只会生成一次该结构。separation进一步限制了一个区域内,有部分位置是无法生成结构的,这使得两个相邻区域内的两个结构不会距离过近。而salt相当于结构的标识符,几乎每个结构都拥有唯一的salt,下面也会讲解,salt还影响了位置的选取。spread有两种,线性(linear)和三角(triangular),前者在区域内均匀选取位置,后者则是均匀选取两次再取平均值,这样会更靠近区域的中心。
例如,堡垒遗迹的spacing是27,separation是4,因此它的一个区域是27x27区块大小,也就是432x432方块大小,separation则表明一个区域中只有23x23区块大小是可以生成结构的。在下图中绿色代表可生成区域,红色代表不可生成。每块绿色区域至多生成一个结构。

这里我整理了各种结构的设定。
十分方便的是,我们能利用库得知一种结构在一个种子中一个区域的位置。首先指定世界种子,然后new一个需要的结构(注意废门有主世界下界之分),然后调用getInRegion,填入世界种子,区域的X,Z坐标,再传入一个ChunkRand(随机数生成器)即可。
这串代码会输出Pos{x=9, y=0, z=3},是区块坐标,可以换算得到方块坐标(144, 0, 48)。
对于埋藏的宝藏和废弃矿井,可以发现spacing是1,separation是0,这表明它们每个Region就是一个区块的大小。
需注意,部分结构有可能会传回null,后续处理坐标时就要判断是否为null。
例如:我们要查找种子114514中,距离原点160以内的宝藏,那么计算过后就知道宝藏区块的坐标cx,cz满足:
写一个循环来遍历这些区块,并判断传回的值,当不是null时输出。
然后程序输出了这些值:
可见这些区块有可能生成宝藏。但事实上没有生成,为什么呢?因为游戏还需要检查相应位置处的群系是否允许生成该结构。对于宝藏,它要求沙滩或者积雪沙滩。我们拥有相应的方法来检查群系。对canSpawn方法传入位置和一个主世界群系(overworldBiomeSource)即可。
经过修改,我们的代码不会输出任何内容,因为确实不存在这样的宝藏。
二、箱子位置和战利品的决定
先从最简单的单箱子结构开始讲解。对宝藏,它的箱子位置所在的区块,就是宝藏结构所在的区块。例如,种子10410544844143616的(2,-4)区块处拥有一个宝藏。MC的逻辑是:宝藏结构先生成,然后生成装饰品,箱子就属于装饰品。因此先将rand的种子设置为装饰种子(decoratorSeed),这个装饰种子的计算方法:对世界种子,区块的起始点的方块坐标,结构对应的装饰盐(salt)进行一些混合运算,具体公式可查看筛种库的源码。需注意,在这里<< 4起到了乘16的作用,就相当于把区块坐标变成了方块坐标;而30001是宝藏的装饰盐,这与上文的位置盐需要区分。之后我会贴出装饰盐的列表。随后对rand呼叫nextLong,这就是lootTableSeed了。可见是-7766255326570070266。
顺便讲解如何方便地用lootTableSeed得到最终战利品。先创建战利品背景(lootContext),传入lootTableSeed,然后调出内置的战利品表,对它使用generate方法,传入lootContext。
可见该宝藏有16个TNT和其它若干物品。
废弃传送门也类似。只需把salt改成40005,以及调整战利品表。但箱子可能会罕见地跨越区块,这导致装饰种子被改成了另一个区块所对应的,因此战利品也被改变了。
对多箱子结构,例如沙漠神殿,它的四个箱子位置所在的区块,都等同于沙漠神殿结构所在的区块。那么我们直接对rand呼叫四次nextLong,它们每一个对应一个箱子。
对复杂的结构,如掠夺者前哨站,它的箱子所在区块与整个结构的朝向有关,因此我们需要额外写一个“前哨站生成器”来模拟朝向,并得到最终箱子所在区块。
对更复杂的结构,如猪灵堡垒,村庄,我们也需要额外写一个生成器(Generator)来模拟得到各个箱子所在的区块坐标,以及对应的战利品表。
例如,在这段代码中,我检查了(0,0)区域是否有猪灵堡垒,然后使用setCarverSeed来确定雕刻种子,随后nextInt决定朝向和种类。2代表藏宝室种类,若不是2我将结束。然后调用下界群系(netherBiomeSource)来确定它是否会因为位于玄武岩三角洲而取消生成。
接下来我使用Xinyu神带领我们写的BastionGenerator来模拟整个猪堡生成,得到了猪堡所有的片段(piece)的位置,种类,朝向。然后我判断是否存在藏宝室底部两个箱子的片段。这些都不重要,我想向你们呈现的是最后一段,如何得到猪堡所有箱子的位置和内容。
我们调用generateLoot方法,就得到了一个列表。列表中的每个元素都是一对坐标和一列物品。然后我遍历这个列表,看看是否有一个箱子拥有钻石镐。这很好理解。
三、各种结构的装饰盐
值得注意的是,这个表格同时也起到提示世界生成顺序的作用。这能解释许多关于结构被“顶掉”的事情。例如,埋藏的宝藏的生成在废弃矿井之后,因此它可能顶掉废弃矿井的内容。

宝藏结构生成时,会用原先该位置的方块覆盖宝藏箱子的各面。这里原先存在的是洞穴蜘蛛的刷怪笼,因此刷怪笼被复制为五个了。然而复制时并没有携带nbt数据,导致刷怪笼只能使用默认nbt数据——猪刷怪笼。此外,因为宝藏箱子继承了原先洞穴蜘蛛刷怪笼的nbt数据,这又使箱子忘记了自己是箱子,因此无法被人打开,而且内容也是空的。
这么神奇的种子是怎么发现的呢?本期点赞过0,投币过0,收藏过0,转发过0,我之后就会写一篇文章来讲解五猪刷怪笼发现的历史。但下期我想先写一下如何通过反转lootTableSeed来得到逆天战利品。54黑曜石,16TNT,都是来源于此。