用户:MashKJo/1.21.1模组开发教程/6.2.方块掉落

MashKJo留言 | 贡献2025年9月7日 (日) 20:14的版本 (创建页面,内容为“== 方块的战利品表 == 你可能注意到了,你的新方块在生存模式下被挖掘,什么都不会掉落,这是因为该方块还没有对应的战利品表。 方块的战利品表位于路径<code>data/<modid>/loot_table/blocks</code>下(没错,这下block又是复数形式了,Mojang一会用单数一会用复数着实让人火大),对于一般的方块,一般就是掉落它的物品形式,没啥特殊的,因此我们可以仿…”)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)

方块的战利品表

你可能注意到了,你的新方块在生存模式下被挖掘,什么都不会掉落,这是因为该方块还没有对应的战利品表。

方块的战利品表位于路径data/<modid>/loot_table/blocks下(没错,这下block又是复数形式了,Mojang一会用单数一会用复数着实让人火大),对于一般的方块,一般就是掉落它的物品形式,没啥特殊的,因此我们可以仿照原版的绝大多数方块战利品表写一个:

{
 "type": "minecraft:block",
 "pools": [
   {
     "bonus_rolls": 0.0,
     "conditions": [
       {
         "condition": "minecraft:survives_explosion"
       }
     ],
     "entries": [
       {
         "type": "minecraft:item",
         "name": "<modid>:<name>"
       }
     ],
     "rolls": 1.0
   }
 ],
 "random_sequence": "<modid>:blocks/<name>"
}

数据生成

战利品表对应的DataProvider是LootTableProvider,使用它并非要覆写方法,它的构造器接受List<LootTableProvider.SubProviderEntry>——没错,我们实际使用的是LootTableSubProvider,毕竟战利品表的类型有很多,有给方块掉落用的,还有给实体掉落用的,还有给箱子战利品生成用的,它们都是战利品表的“子”(Sub)类型。

我们要生成的是方块掉落战利品表,因此要用到的LootTableSubProvider实现类是BlockLootSubProvider:

public class MyBlockLootSubProvider extends BlockLootSubProvider {
   public MyBlockLootSubProvider(HolderLookup.Provider lookupProvider) {
       //第一个参数是Set<Item>,意为“能在爆炸中保存下来的物品类型”,这里我们填入一个空集合。
       super(Set.of(), FeatureFlags.DEFAULT_FLAGS, lookupProvider);
   }

   //这个方法用于获取所有生成战利品表的方块,既然是给我们的模组datagen,这里不能填写BuiltInRegistries.BLOCK。
   @Override
   protected Iterable<Block> getKnownBlocks() {
       return MyBlockClass.BLOCKS.getEntries()
           .stream()
           .map(holder -> (Block)holder.value())
           .toList();
   }

   @Override
   protected void generate() {
       //最简单的例子:掉落自身的物品形式
       dropSelf(MyBlockClass.DROP_SELF_BLOCK.get());

       //掉落别的物品。
       dropOther(MyBlockClass.DROP_OTHER_BLOCK.get(), Items.DIAMOND);

       //自定义复杂的战利品表。
       //第二个参数是Function<Block, LootTable.Builder>。
       add(MyBlockClass.BLOCK_WITH_COMPLEX_DROP.get(),
           block -> aCertainLootTableBuilder);
   }
}

然后就能在GatherDataEvent的监听器中添加LootTableProvider了:

//这里的LootParamSets.BLOCK是战利品上下文参数列表,由于生成的是方块掉落战利品表,因此填BLOCK,填其他的可能会出现缺失必要的战利品上下文参数的情况,导致runData失败。
event.addProvider(new LootTableProvider(output, Set.of(), List.of(
   new LootTableProvider.SubProviderEntry(MyBlockLootSubProvider::new, LootParamSets.BLOCK)), lookupProvider));

挖掘工具与挖掘等级

Minecraft原版提供了4个与挖掘工具相关的方块标签:minecraft:mineable/axe、minecraft:mineable/hoe、minecraft:mineable/pickaxe和minecraft:mineable/shovel,分别代表方块在被斧头、锄头、镐头和铲子挖掘时挖掘速度会提高。如果你想要方块被特定种类的工具挖掘时才会掉落(即对应的战利品表才会生效),那么你需要在传入的BlockBehaviour.Properties中调用#requiresCorrectToolForDrops。

在Minecraft 1.21中,挖掘等级也标签化了,例如,如果我想要让某方块至少被铁制工具挖掘才会掉落,则只需将方块加入标签minecraft:needs_iron_tool即可,读者可以自行查看原版的相关方块标签。

#getDrops

如果我想要更加动态的方块掉落,如依据方块状态,甚至破坏前方块持有的方块实体来决定掉落呢?一个方法是自己写一个符合自己需求的战利品表函数类型,但你如果不想考虑对数据包的兼容性,或者干脆就不想让数据包修改掉落,你可以直接覆写BlockBehaviour#getDrops:

@Override
protected List<ItemStack> getDrops(BlockState state, LootParams.Builder builder) {
   //由这个LootParams.Builder,你可以获取到方块的位置,及该方块持有的方块实体(如果有的话)。
   ...
}

修改已有方块的掉落物

要修改已有方块的掉落物,战利品表覆盖就能做到这一点,但其兼容性毕竟等于没有,因此最好的方法是:

  • 监听BlockDropsEvent
  • 或者,应用NeoForge的全局战利品修饰符(Global Loot Modifier)到相应的方块战利品表