战利品表(Loot Tables)
战利品表(Loot Tables)是用于定义随机化掉落物的数据文件。可以掷出一个战利品表,返回一个(可能为空的)物品堆列表。此过程的输出取决于(伪)随机性。战利品表位于 data/<mod_id>/loot_table/<name>.json。例如,由泥土(dirt)方块使用的战利品表 minecraft:blocks/dirt 位于 data/minecraft/loot_table/blocks/dirt.json。
Minecraft在游戏中的多个地方使用战利品表,包括方块(block)掉落、实体(entity)掉落、箱子战利品、钓鱼战利品以及许多其他场合。如何引用战利品表取决于上下文:
- 默认情况下,每个方块都会获得一个关联的战利品表,位于
<block_namespace>:blocks/<block_name>。可以通过在方块的Properties上调用#noLootTable来禁用,导致不创建战利品表且方块不掉落任何物品;这主要是由类空气或技术性方块完成的。 - 每个未调用
EntityType.Builder#noLootTable(通常是MobCategory#MISC中的实体)的实体,默认都会获得一个关联的战利品表,位于<entity_namespace>:entities/<entity_name>。可以通过重写#getLootTable来更改。例如,绵羊利用此特性根据羊毛颜色掷出不同的战利品表。 - 结构中的箱子在其方块实体数据中指定它们的战利品表。Minecraft将所有箱子战利品表存储在
minecraft:chests/<chest_name>;建议但非强制要求模组遵循此做法。 - 村民在袭击后可能向玩家投掷的礼物物品的战利品表定义在
neoforge:raid_hero_gifts数据映射中。 - 其他战利品表,例如钓鱼战利品表,在需要时从
level.getServer().reloadableRegistries().getLootTable(lootTableKey)获取。所有原版战利品表位置的列表可以在BuiltInLootTables中找到。
通常只应为你模组所属的内容创建战利品表。对于修改现有战利品表,应改用全局战利品修改器(GLMs)。
由于战利品表系统的复杂性,战利品表由几个子系统组成,每个都有不同的目的。
战利品条目(Loot Entry)
战利品条目(或称战利品池条目),在代码中由抽象类 LootPoolEntryContainer 表示,是一个单一的掉落元素。它可以指定一个或多个要掉落的物品。
原版总共提供了8种不同的战利品条目类型。通过公共的 LootPoolEntryContainer 超类,它们都具有以下属性:
weight:权重值。默认为1。用于某些物品应比其他物品更常见的情况。例如,给定两个战利品条目,一个权重为3,另一个为1,那么第一个条目被选中的几率为75%,第二个为25%。quality:品质值。默认为0。如果非零,则此值乘以运气值(在战利品上下文(context)中设置)并在掷战利品表时加到权重上。conditions:应用于此战利品条件的战利品条件(loot conditions)列表。如果一个条件失败,该条目将被视为不存在。functions:应用 于此战利品条目输出的战利品函数(loot functions)列表。
战利品条目通常分为两组:单例(singletons)(公共超类为 LootPoolSingletonContainer)和复合(composites)(公共超类为 CompositeEntryBase),其中复合条目由多个单例条目组成。Minecraft提供了以下单例类型:
minecraft:empty:一个空的战利品条目,代表没有物品。在代码中通过调用EmptyLootItem#emptyItem创建。minecraft:item:一个单一的战利品物品条目,掷出时掉落指定的物品。在代码中通过调用LootItem#lootTableItem并传入所需物品创建。- 设置堆叠大小、数据组件等可以通过战利品函数完成。
minecraft:tag:一个标签(tag)条目,掷出时掉落指定标签中的所有物品。根据布尔属性expand的值有两种变体。如果expand为真,则为标签中的每个物品生成一个单独的条目;否则,使用一个条目来掉落所有物品。通过调用TagEntry#tagContents(对应expand=false)或TagEntry#expandTag(对应expand=true)创建,每个都带有一个物品标签键(tag key)参数。- 例如,如果
expand为真且标签是#minecraft:planks,则为每种木板类型生成一个条目(所以原版11种木板有11个条目 + 每个模组添加的木板一个条目),每个条目具有指定的权重、品质和函数;而如果expand为假,则使用一个掉落所有木板的单一条目。
- 例如,如果
minecraft:dynamic:引用动态掉落(dynamic drop)的战利品条目。动态掉落是一个系统,用于向战利品表中添加无 法事先指定的条目,而是在代码中添加。动态掉落条目由一个id和一个实际添加物品的Consumer<ItemStack>组成。要添加动态掉落条目,需指定一个具有所需id的minecraft:dynamic条目,然后在战利品上下文(context)中添加相应的消费者(consumer)。使用DynamicLoot#dynamicEntry创建。minecraft:loot_table:一个掷出另一个战利品表的战利品条目,将该战利品表的结果作为单个条目添加。另一个战利品表可以通过id指定,也可以整个内联。在代码中通过调用NestedLootTable#lootTableReference并传入一个ResourceLocation参数来创建引用,或调用NestedLootTable#inlineLootTable并传入一个LootTable对象参数来创建内联战利品表。
Minecraft提供了以下复合类型:
minecraft:group:包含其他战利品条目列表的战利品条目,这些条目按顺序运行。在代码中通过调用EntryGroup#list创建,或通过在其他LootPoolSingletonContainer.Builder上调用#append创建,每个都带有其他战利品条目构建器。minecraft:sequence:类似minecraft:group,但战利品条目在一个子条目失败时停止运行,丢弃之后的所有条目。在代码中通过调用SequentialEntry#sequential创建,或通过在其他LootPoolSingletonContainer.Builder上调用#then创建,每个都带有其他战利品条目构建器。minecraft:alternatives:与minecraft:sequence有些相反,但战利品条目在一个子条目成功(而不是失败)时停止运行,丢弃之后的所有条目。在代码中通过调用AlternativesEntry#alternatives创建,或通过在其他LootPoolSingletonContainer.Builder上调用#otherwise创建,每个都带有其他战利品条目构建器。
对于模组开发者,也可以定义自定义战利品条目类型。
战利品池(Loot Pool)
战利品池本质上是战利品条目的列表。战利品表可以包含多个战利品池,每个战利品池将独立于其他池被掷出。
战利品池可能包含以下内容:
entries:战利品条目列表。conditions:应用于此战利品池的战利品条件(loot conditions)列表。如果一个条件失败,则不会掷出该池的任何条目。functions:应用于此战利品池所有战利品条目输出的战利品函数(loot functions)列表。rolls和bonus_rolls:两个数字提供器(见下文),它们共同决定此战利品池将被掷出的次数。公式为 rolls + bonus_rolls * luck,其中 luck 值在战利品参数(parameters)中设置。name:战利品池的名称。NeoForge添加。可被GLMs使用。如果未指定,则为战利品池的哈希码,前缀为custom#。
数字提供器(Number Provider)
数字提供器(Number Providers)是一种在数据包上下文中获取(伪)随机数字的方式。主要由战利品表使用,它们也用于其他上下文,例如在世界生成中。原版提供了以下六种数字提供器:
minecraft:constant:一个恒定的浮点值,需要时舍入为整数。通过ConstantValue#exactly创建。minecraft:uniform:均匀分布的随机整数或浮点值,设置最小和最大值。最小和最大值之间的所有值出现的几率相同。通过UniformGenerator#between创建。minecraft:binomial:二项式分布的随机整数值,设置 n 和 p 值。有关这些值的含义,请参阅二项分布(Binomial Distribution)。通过BinomialDistributionGenerator#binomial创建。minecraft:score:给定一个实体目标(entity target)、一个计分项名称和(可选的)一个缩放值,获取该实体目标的给定记分板值,并乘以给定的缩放值(如果可用)。通过ScoreboardValue#fromScoreboard创建。minecraft:storage:来自给定NBT路径的命令存储(storage)中的值。通过new StorageValue创建。minecraft:enchantment_level:提供每个附魔等级的值。通过EnchantmentLevelProvider#forEnchantmentLevel创建,提供一个LevelBasedValue。有效的LevelBasedValue有:- 简单地是一个常数值,没有指定类型。通过
LevelBasedValue#constant创建。 minecraft:linear:每附魔等级线性增加的值,加上一个可选的恒定基础值。通过LevelBasedValue#perLevel创建。minecraft:levels_squared:将附魔值平方,然后加上一个可选的基值。通过new LevelBasedValue.LevelsSquared创建。minecraft:fraction:接受另外两个LevelBasedValue,使用它们创建一个分数。通过new LevelBasedValue.Fraction创建。minecraft:clamped:接受另一个LevelBasedValue,以及最小和最大值。使用另一个LevelBasedValue计算值并钳制结果。通过new LevelBasedValue.Clamped创建。minecraft:lookup:接受一个List<Float>和一个回退LevelBasedValue。在列表中查找要使用的值(等级1是列表中的第一个元素,等级2是第二个元素,等等),如果某个等级的值缺失,则使用回退值。通过LevelBasedValue#lookup创建。
- 简单地是一个常数值,没有指定类型。通过
如果需要,模组开发者也可以注册自定义数字提供器和自定义基于等级的值。
战利品参数(Loot Parameters)
战利品参数(Loot Parameter),内部称为 ContextKey<T>,是在掷战利品表时提供给战利品表的一个参数,其中 T 是所提供参数的类型,例如 BlockPos 或 Entity。它们可以被战利品条件(loot conditions)和战利品函数(loot functions)使用。例如,minecraft:killed_by_player 战利品条件检查是否存在 minecraft:player 参数。
Minecraft提供了以下战利品参数:
minecraft:origin:与战利品表关联的位置,例如箱子战利品的位置。通过LootContextParams.ORIGIN访问。minecraft:tool:与战利品表关联的物品堆,例如用于破坏方块的物品。这不一定是工具。通过LootContextParams.TOOL访问。minecraft:enchantment_level:一个附魔等级,用于附魔逻辑。通过LootContextParams.ENCHANTMENT_LEVEL访问。minecraft:enchantment_active:使用的物品是否有附魔,例如用于精准采集检查。通过LootContextParams.ENCHANTMENT_ACTIVE访问。minecraft:block_state:与战利品表关联的方块状态,例如被破坏的方块状态。通过LootContextParams.BLOCK_STATE访问。minecraft:block_entity:与战利品表关联的方块实体,例如与被破坏方块关联的方块实体。例如被潜影盒用于将其库存保存到掉落物中。通过LootContextParams.BLOCK_ENTITY访问。minecraft:explosion_radius:当前上下文中的爆炸半径。主要用于对掉落物应用爆炸衰减。通过LootContextParams.EXPLOSION_RADIUS访问。minecraft:this_entity:与战利品表关联的实体,通常是被杀死的实体。通过LootContextParams.THIS_ENTITY访问。minecraft:damage_source:与战利品表关联的伤害源(damage source),通常是杀死实体的伤害源。通过LootContextParams.DAMAGE_SOURCE访问。minecraft:attacking_entity:与战利品表关联的攻击实体,通常是实体的杀手。通过LootContextParams.ATTACKING_ENTITY访问。minecraft:direct_attacking_entity:与战利品表关联的直接攻击实体。例如,如果攻击实体是骷髅,直接攻击实体就是箭。通过LootContextParams.DIRECT_ATTACKING_ENTITY访问。minecraft:last_damage_player:与战利品表关联的玩家,通常是最后攻击被杀实体的玩家,即使玩家的击杀是间接的(例如:玩家轻击了实体,然后它被尖刺杀死)。例如用于仅限玩家击杀的掉落物。通过LootContextParams.LAST_DAMAGE_PLAYER访问。
可以通过使用所需的id调用 new ContextKey<T> 来创建自定义战利品参数。由于它们仅仅是资源定位符的包装器,因此不需要注册。
实体目标(Entity Targets)
实体目标(Entity Targets)是战利品条件和函数中使用的一种类型,在代码中由 LootContext.EntityTarget 枚举表示。用于指定在条件或函数上下文中要查询的实体战利品参数。有效值为:
"this"或LootContext.EntityTarget.THIS:代表"minecraft:this_entity"参数。"attacker"或LootContext.EntityTarget.ATTACKER:代表"minecraft:attacking_entity"参数。"direct_attacker"或LootContext.EntityTarget.DIRECT_ATTACKER:代表"minecraft:direct_attacking_entity"参数。"attacking_player"或LootContext.EntityTarget.ATTACKING_PLAYER:代表"minecraft:last_damage_player"参数。
例如,minecraft:entity_properties 战利品条件接受一个实体目标,以便在需要时检查所有四个战利品参数。
战利品参数集(Loot Parameter Sets)
战利品参数集(Loot Parameter Sets),也称为战利品表类型,在代码中称为 ContextKeySet,是必需和可选战利品参数的集合。尽管名称如此,它们并不是 Set(甚至不是 Collection)。相反,它们是包装在两个 Set<ContextKey<?>> 周围,一个保存必需参数(#required),一个保存可选参数(#allowed)。它们用于验证战利品参数的使用者只使用预期可用的参数,并在掷表时验证必需参数是否存在。除此之外,它们还用于进度和附魔逻辑。
原版提供了以下战利品参数集(必需参数为粗体,可选参数为斜体;代码中的名称是 LootContextParamSets 中的常量):
| ID | 代码中名称 | 指定的战利品参数 | 用途 |
|---|---|---|---|
minecraft:empty | EMPTY | 无 | 回退目的。 |
minecraft:generic | ALL_PARAMS | minecraft:origin, minecraft:tool, minecraft:block_state, minecraft:block_entity, minecraft:explosion_radius, minecraft:this_entity, minecraft:damage_source, minecraft:attacking_entity, minecraft:direct_attacking_entity, minecraft:last_damage_player | 验证。 |
minecraft:command | COMMAND | minecraft:origin, minecraft:this_entity | 命令。 |
minecraft:selector | SELECTOR | minecraft:origin, minecraft:this_entity | 命令中的实体选择器。 |
minecraft:block | BLOCK | minecraft:origin, minecraft:tool, minecraft:block_state, minecraft:block_entity, minecraft:explosion_radius, minecraft:this_entity | 方块破坏。 |
minecraft:block_use | BLOCK_USE | minecraft:origin, minecraft:block_state, minecraft:this_entity | 无原版用途。 |
minecraft:hit_block | HIT_BLOCK | minecraft:origin, minecraft:enchantment_level, minecraft:block_state, minecraft:this_entity | 引雷(Channeling)附魔。 |
minecraft:chest | CHEST | minecraft:origin, minecraft:this_entity, minecraft:attacking_entity | 战利品箱和类似容器、战利品箱矿车。 |
minecraft:archaeology | ARCHAEOLOGY | minecraft:origin, minecraft:this_entity, minecraft:tool | 考古。 |
minecraft:vault | VAULT | minecraft:origin, minecraft:this_entity, minecraft:tool | 试炼密室(Trial Chamber)宝库奖励。 |
minecraft:entity | ENTITY | minecraft:origin, minecraft:this_entity, minecraft:damage_source, minecraft:attacking_entity, minecraft:direct_attacking_entity, minecraft:last_damage_player | 实体击杀。 |
minecraft:shearing | SHEARING | minecraft:origin, minecraft:this_entity, minecraft:tool | 为实体剪毛,例如绵羊。 |
minecraft:equipment | EQUIPMENT | minecraft:origin, minecraft:this_entity | 实体装备,例如僵尸。 |
minecraft:gift | GIFT | minecraft:origin, minecraft:this_entity | 袭击英雄礼物。 |
minecraft:barter | PIGLIN_BARTER | minecraft:this_entity | 猪灵以物易物。 |
minecraft:fishing | FISHING | minecraft:origin, minecraft:tool, minecraft:this_entity, minecraft:attacking_entity | 钓鱼。 |
minecraft:enchanted_item | ENCHANTED_ITEM | minecraft:tool, minecraft:enchantment_level | 若干附魔。 |
minecraft:enchanted_entity | ENCHANTED_ENTITY | minecraft:origin, minecraft:enchantment_level, minecraft:this_entity | 若干附魔。 |
minecraft:enchanted_damage | ENCHANTED_DAMAGE | minecraft:origin, minecraft:enchantment_level, minecraft:this_entity, minecraft:damage_source, minecraft:attacking_entity, minecraft:direct_attacking_entity | 伤害和保护附魔。 |
minecraft:enchanted_location | ENCHANTED_LOCATION | minecraft:origin, minecraft:enchantment_level, minecraft:enchantment_active, minecraft:this_entity | 冰霜行者(Frost Walker)和灵魂疾行(Soul Speed)附魔。 |
minecraft:advancement_entity | ADVANCEMENT_ENTITY | minecraft:origin, minecraft:this_entity | 若干进度标准(advancement criteria)。 |
minecraft:advancement_location | ADVANCEMENT_LOCATION | minecraft:origin, minecraft:tool, minecraft:block_state, minecraft:this_entity | 若干进度触发器(advancement triggers)。 |
minecraft:advancement_reward | ADVANCEMENT_REWARD | minecraft:origin, minecraft:this_entity | 进度奖励(Advancement rewards)。 |
战利品上下文(Loot Context)
战利品上下文(Loot Context)是一个包含掷战利品表时情境信息的对象。这些信息包括:
- 掷战利品表所在的
ServerLevel。通过#getLevel获取。 - 用于掷战利品表的
RandomSource。通过#getRandom获取。 - 战利品参数。使用
#hasParameter检查是否存在,使用#getParameter获取单个参数。 - 运气值,用于计算奖励掷骰次数和品质值。通常通过实体的运气属性填充。通过
#getLuck获取。 - 动态掉落消费者。参见上文获取更多信息。通过
#addDynamicDrops设置。无获取方法。
战利品表(Loot Table)
结合所有前面的元素,我们最终得到了战利品表。战利品表JSON可以指定以下值:
pools:战利品池列表。neoforge:conditions:数据加载条件(data load conditions)列表。警告:这些是数据加载条件,不是战利品条件(loot conditions)!functions:应用于此战利品表所有战利品条目输出的战利品函数(loot functions)列表。type:一个战利品参数集,用于验证对战利品参数的正确使用。可选;如果缺失,将跳过验证。random_sequence:此战利品表的随机序列,以资源定位符形式表示。随机序列由Level提供,用于在相同条件下实现一致的战利品表掷骰。这通常使用战利品表的位置。
一个示例战利品表可能具有以下格式:
{
"type": "chest", // 战利品参数集
"neoforge:conditions": [
// 数据加载条件
],
"functions": [
// 表级战利品函数
],
"pools": [ // 战利品池列表
{
"rolls": 1, // 战利品表的掷骰次数,这里用5将导致从池中获得5个结果
"bonus_rolls": 0.5, // 奖励掷骰次数
"name": "my_pool",
"conditions": [
// 池级战利品条件
],
"functions": [
// 池级战利品函数
],
"entries": [ // 战利品表条目列表
{
"type": "minecraft:item", // 战利品条目类型
"name": "minecraft:dirt", // 类型特定属性,例如物品的名称
"weight": 3, // 条目的权重
"quality": 1, // 条目的品质
"conditions": [
// 条目级战利品条件
],
"functions": [
// 条目级战利品函数
]
}
]
}
]
}
掷出战利品表(Rolling a Loot Table)
要掷出战利品表,我们需要两样东西:战利品表本身和一个战利品上下文。
让我们从获取战利品表本身开始。我们可以使用 level.getServer().reloadableRegistries().getLootTable(lootTableId) 获取战利品表。由于战利品数据仅通过服务器可用,此逻辑必须在逻辑服务器(logical server)上运行,而不是逻辑客户端。
Minecraft内置的战利品表ID可以在 BuiltInLootTables 类中找到。方块战利品表可以通过 BlockBehaviour#getLootTable 获取,实体战利品表可以通过 EntityType#getDefaultLootTable 或 Entity#getLootTable 获取。
现在我们有战利品表了,让我们构建我们的参数集。我们首先创建一个 LootParams.Builder 的实例:
// 确保你在服务器上,否则转换将失败。
LootParams.Builder builder = new LootParams.Builder((ServerLevel) level);
然后我们可以添加战利品上下文参数,像这样:
// 使用你需要的任何上下文参数和值。原版参数可以在 LootContextParams 中找到。
builder.withParameter(LootContextParams.ORIGIN, position);
// 此变体可以接受 null 作为值,在这种情况下,该参数的现有值将被移除。
builder.withOptionalParameter(LootContextParams.ORIGIN, null);
// 添加一个动态掉落。
builder.withDynamicDrop(ResourceLocation.fromNamespaceAndPath("examplemod", "example_dynamic_drop"), stackAcceptor -> {
// 一些逻辑在这里
});
// 设置我们的运气值。假设有一个玩家可用。没有玩家的上下文应在此处使用0。
builder.withLuck(player.getLuck());
最后,我们可以从构建器创建 LootParams 并用它们来掷出战利品表:
// 如果需要,在此指定一个战利品上下文参数集。
LootParams params = builder.create(LootContextParamSets.EMPTY);
// 获取战利品表。
LootTable table = level.getServer().reloadableRegistries().getLootTable(location);
// 实际掷出战利品表。
List<ItemStack> list = table.getRandomItems(params);
// 如果你是为容器内容(例如箱子战利品)掷出战利品表,请改用此方法。
// 此方法负责将战利品物品正确分配到容器中。
List<ItemStack> containerList = table.fill(container, params, someSeed);
LootTable 还公开了一个名为 #getRandomItemsRaw 的方法。与各种 #getRandomItems 变体不同,#getRandomItemsRaw 方法不会应用全局战利品修改器(GLMs)。仅在你清楚自己在做什么时才使用此方法。
数据生成(Datagen)
战利品表可以通过注册一个 LootTableProvider 并在构造函数中提供 LootTableSubProvider 列表来进行数据生成(datagenned):
@SubscribeEvent // 在模组事件总线上
public static void onGatherData(GatherDataEvent.Client event) {
// 如果添加数据包对象,先调用 event.createDatapackRegistryObjects(...)
event.createProvider((output, lookupProvider) -> new LootTableProvider(
output,
// 所需的表资源定位符集合。这些随后会被验证是否存在。
// 通常不建议模组验证存在性,
// 因此我们传入一个空集合。
Set.of(),
// 子提供器条目列表。有关此处使用的值,请参见下文。
List.of(...),
// 注册表访问器
lookupProvider
));
}
LootTableSubProviders
LootTableSubProviders 是实际生成发生的地方。首先,我们实现 LootTableSubProvider 并重写 #generate:
public class MyLootTableSubProvider implements LootTableSubProvider {
// 该参数由lambda提供(见下文)。可以存储并用于查找其他注册表条目。
public MyLootTableSubProvider(HolderLookup.Provider lookupProvider) {
// 将 lookupProvider 存储在字段中
}
@Override
public void generate(BiConsumer<ResourceLocation, LootTable.Builder> consumer) {
// LootTable.lootTable() 返回一个我们可以添加战利品表的战利品表构建器。
consumer.accept(ResourceLocation.fromNamespaceAndPath(ExampleMod.MOD_ID, "example_loot_table"), LootTable.lootTable()
// 添加一个战利品表级别的战利品函数。此示例使用了一个数字提供器(见下文)。
.apply(SetItemCountFunction.setCount(ConstantValue.exactly(5)))
// 添加一个战利品池。
.withPool(LootPool.lootPool()
// 添加一个战利品池级别的函数,与上面类似。
.apply(...)
// 添加一个战利品池级别的条件。此示例仅在雨天时掷出该池。
.when(WeatherCheck.weather().setRaining(true))
// 分别设置掷骰次数和奖励掷骰次数。
// 这两种方法都使用了数字提供器。
.setRolls(UniformGenerator.between(5, 9))
.setBonusRolls(ConstantValue.exactly(1))
// 添加一个战利品条目。此示例返回一个物品战利品条目。更多战利品条目见下文。
.add(LootItem.lootTableItem(Items.DIRT))
)
);
}
}
一旦我们有了战利品表子提供器,我们将其添加到战利品提供器的构造函数中,像这样:
new LootTableProvider(output, Set.of(), List.of(
new SubProviderEntry(
// 对子提供器构造函数的引用。
// 这是一个 Function<HolderLookup.Provider, ? extends LootTableSubProvider>。
MyLootTableSubProvider::new,
// 关联的战利品上下文集。如果不确定用什么,就用 empty。
LootContextParamSets.EMPTY
),
// 其他子提供器在此(如果适用)
), lookupProvider
);
BlockLootSubProvider
BlockLootSubProvider 是一个抽象的辅助类,包含许多用于创建常见方块战利品表的辅助方法,例如单一物品掉落(#createSingleItemTable)、掉落创建该表的方块(#dropSelf)、仅精准采集掉落(#createSilkTouchOnlyTable)、台阶类方块的掉落(#createSlabItemTable)以及许多其他。不幸的是,为模组用途设置 BlockLootSubProvider 涉及更多样板代码:
public class MyBlockLootSubProvider extends BlockLootSubProvider {
// 如果此类是你的战利品表提供器的内部类,则构造函数可以是私有的。
// 参数由 LootTableProvider 构造函数中的 lambda 提供。
public MyBlockLootSubProvider(HolderLookup.Provider lookupProvider) {
// 第一个参数是我们正在为其创建战利品表的方块集合。我们不进行硬编码,
// 而是使用我们的方块注册器,并在此传入一个空集合。
// 第二个参数是特性标志集,这将是默认标志,
// 除非你正在添加自定义标志(这超出了本文的范围)。
super(Set.of(), FeatureFlags.DEFAULT_FLAGS, lookupProvider);
}
// 此 Iterable 的内容用于验证。
// 我们在此返回遍历我们方块注册器值的 Iterable。
@Override
protected Iterable<Block> getKnownBlocks() {
// 我们的 DeferredRegister 的内容。
return MyRegistries.BLOCK_REGISTRY.getEntries()
.stream()
// 在此转换为 Block,否则它将是 ? extends Block 而 Java 会报错。
.map(e -> (Block) e.value())
.toList();
}
// 实际添加我们的战利品表。
@Override
protected void generate() {
// 等同于调用 add(MyBlocks.EXAMPLE_BLOCK.get(), createSingleItemTable(MyBlocks.EXAMPLE_BLOCK.get()));
this.dropSelf(MyBlocks.EXAMPLE_BLOCK.get());
// 添加一个仅精准采集的战利品表。
this.add(MyBlocks.EXAMPLE_SILK_TOUCHABLE_BLOCK.get(),
this.createSilkTouchOnlyTable(MyBlocks.EXAMPLE_SILK_TOUCHABLE_BLOCK.get()));
// 其他战利品表添加在此
}
}
然后,我们像添加任何其他子提供器一样,将子提供器添加到战利品表提供器的构造函数中:
new LootTableProvider(output, Set.of(), List.of(new SubProviderEntry(
MyBlockLootTableSubProvider::new,
LootContextParamSets.BLOCK // 在此使用 BLOCK 是有意义的
)), lookupProvider
);
EntityLootSubProvider
与 BlockLootSubProvider 类似,EntityLootSubProvider 为实体战利品表生成提供了许多辅助方法。也类似于 BlockLootSubProvider,我们必须提供已知实体的 Stream<EntityType<?>>(而不是之前使用的 Iterable<Block>)。总体而言,我们的实现看起来与 BlockLootSubProvider 非常相似,但所有提到的方块都替换为实体类型:
public class MyEntityLootSubProvider extends EntityLootSubProvider {
public MyEntityLootSubProvider(HolderLookup.Provider lookupProvider) {
// 与方块不同,我们不提供已知实体类型集合。原版在这里使用自定义检查。
super(FeatureFlags.DEFAULT_FLAGS, lookupProvider);
}
// 这个类使用 Stream 而不是 Iterable,所以我们需要稍微调整一下。
@Override
protected Stream<EntityType<?>> getKnownEntityTypes() {
return MyRegistries.ENTITY_TYPES.getEntries()
.stream()
.map(e -> (EntityType<?>) e.value());
}
@Override
protected void generate() {
this.add(MyEntities.EXAMPLE_ENTITY.get(), LootTable.lootTable());
// 其他战利品表添加在此
}
}
然后,我们再次将子提供器添加到战利品表提供器的构造函数中:
new LootTableProvider(output, Set.of(), List.of(new SubProviderEntry(
MyEntityLootTableSubProvider::new,
LootContextParamSets.ENTITY
)), lookupProvider
);