全局战利品修改器
全局战利品修改器以数据驱动方式修改采集掉落,无需覆盖几十乃至数百个原版战利品表,也能在不知道加载了哪些 Mod 的情况下处理与其他 Mod 战利品表相关的效果。它们像标签一样可以叠加,而非后加载者覆盖前者。
原创说明图,依据 Forge 1.21.x 官方文档 绘制。
注册全局战利品修改器
需要四项内容:
- 创建
global_loot_modifiers.json,告知 Forge 你的修改器,工作方式类似标签。 - 一个表示修改器的序列化 JSON,包含修改数据,允许数据包调整效果。
- 实现
IGlobalLootModifier的类,包含使修改器工作的操作代码。大多数开发者可以继承提供基础功能的LootModifier。 - 编码和解码操作类的 codec,像其他
IForgeRegistryEntry一样注册。
global_loot_modifiers.json
此文件表示要加载到游戏中的所有战利品修改器,必须位于 data/forge/loot_modifiers/global_loot_modifiers.json。它只从 forge 命名空间读取,放在 Mod 自己的命名空间下会被忽略。
entries 是待加载修改器的有序列表。ResourceLocation 指向 data/<namespace>/loot_modifiers/<path>.json 中对应条目,主要供数据包作者解决不同 Mod 修改器的冲突。
replace 为 true 时,不再追加修改器,而是完全替换全局列表。为了兼容其他 Mod,开发者通常应使用 false;数据包作者可能希望使用 true 进行覆盖。
带注释和省略号的示例用于说明结构;实际 JSON 文件需移除注释并补全内容。
{
"replace": false, // 必须提供
"entries": [
// 表示 data/examplemod/loot_modifiers/example_glm.json 中的修改器
"examplemod:example_glm",
"examplemod:example_glm2" // ...
]
}
序列化 JSON
此文件包含修改器的所有潜在变量,包括修改战利品前必须满足的条件。应尽可能避免硬编码,使数据包作者能够按需调整平衡。
type 表示用于读取 JSON 的 codec 的注册表名称,必须存在。conditions 表示修改器激活所需的战利品表条件,也必须存在;应避免硬编码,给数据包作者调整条件的灵活性。
重要:只有使用 Forge 附带类时,
conditions才按上述方式表示激活条件。继承LootModifier时,所有条件以 AND 组合并检查,以决定是否应用修改器。
还可以指定由序列化器读取、由修改器定义的其他属性。
// 在 data/examplemod/loot_modifiers/example_glm.json 中
{
"type": "examplemod:example_loot_modifier",
"conditions": [
// 普通战利品表条件
// ...
],
"prop1": "val1",
"prop2": 10,
"prop3": "minecraft:dirt"
}
IGlobalLootModifier
要提供全局战利品修改器的功能,必须实现 IGlobalLootModifier。序列化器每次从 JSON 解码信息并将其提供给对象时都会生成实例。
新修改器需要定义 #apply 和 #codec。#apply 接收当前将生成的战利品,以及当前世界或额外参数等上下文信息,返回要生成的掉落物列表。一个修改器返回的列表按注册顺序传给其他修改器,因此修改后的战利品还可能被另一个修改器修改。#codec 返回用于在 JSON 和修改器之间编码、解码的已注册 codec。
LootModifier 子类
LootModifier 是 IGlobalLootModifier 的抽象实现,提供大多数开发者可轻松继承的基础功能。它定义 #apply 检查条件,决定是否修改生成的战利品。
子类中需注意两点:构造函数必须接收 LootItemCondition 数组,以及 #doApply 方法。条件数组定义修改前必须满足的条件,所有条件以 AND 组合,即全部必须为真。#doApply 与 #apply 类似,但只在所有条件为真时执行。
public class ExampleModifier extends LootModifier {
public ExampleModifier(LootItemCondition[] conditionsIn, String prop1, int prop2, Item prop3) {
super(conditionsIn);
// 保存其余参数
}
@NotNull
@Override
protected ObjectArrayList<ItemStack> doApply(ObjectArrayList<ItemStack> generatedLoot, LootContext context) {
// 修改战利品并返回新掉落物
}
@Override
public Codec<? extends IGlobalLootModifier> codec() {
// 返回用于编码和解码此修改器的 codec
}
}
战利品修改器 Codec
JSON 与 IGlobalLootModifier 实例通过 Codec 连接,T 表示所用修改器类型。
为方便使用,LootModifier#codecStart 提供战利品条件 codec,以便加入类似 record 的 codec。它用于对应修改器的数据生成。
// 对于某个 DeferredRegister<Codec<? extends IGlobalLootModifier>> REGISTRAR
public static final RegistryObject<Codec<ExampleModifier>> EXAMPLE_CODEC = REGISTRAR.register("example_codec", () ->
RecordCodecBuilder.create(
inst -> LootModifier.codecStart(inst).and(
inst.group(
Codec.STRING.fieldOf("prop1").forGetter(m -> m.prop1),
Codec.INT.fieldOf("prop2").forGetter(m -> m.prop2),
ForgeRegistries.ITEMS.getCodec().fieldOf("prop3").forGetter(m -> m.prop3)
)
).apply(inst, ExampleModifier::new)
)
);
原文示例中的字段声明缺少变量名,译文补为 EXAMPLE_CODEC。Forge Git 仓库中有示例,包括精准采集和烧炼效果;原文链接指向 1.20.x。