全局战利品修改器
全局战利品修改器(Global Loot Modifier,简称 GLM)是一种数据驱动的掉落物修改方式。它无需覆盖数十乃至数百张原版战利品表,也能在不知道加载了哪些 Mod 的情况下,实现需要修改其他 Mod 战利品表的效果。
GLM 会先对关联的战利品表进行抽取,再修改所得结果。GLM 的效果可以叠加,而非仅保留最后加载的一项,因此多个 Mod 可以修改同一张表;这一点与标签类似。
注册 GLM 需要四项内容:
global_loot_modifiers.json,位于data/neoforge/loot_modifiers/global_loot_modifiers.json(不是 Mod 自己的命名空间)。它告诉 NeoForge 应应用哪些修改器及其顺序。- 描述修改器的 JSON 文件,包含全部修改数据,让数据包可以调整效果。路径为
data/<namespace>/loot_modifiers/<path>.json。 - 实现
IGlobalLootModifier或继承LootModifier的类(后者也实现了该接口),包含实际修改逻辑。 - 用于编码和解码该类的 map codec,通常作为修改器类中的
public static final字段。
global_loot_modifiers.json
此文件告诉 NeoForge 要对战利品表应用哪些修改器,可包含两个键:
entries:待加载修改器的列表。所列ResourceLocation指向data/<namespace>/loot_modifiers/<path>.json中的对应条目。修改器按列表顺序应用,这在处理 Mod 兼容问题时可能很重要。replace:是否替换旧修改器(true),还是追加到现有列表(false)。它类似标签的同名键,但此处是必填项。Mod 开发者通常应使用false;true主要面向整合包或数据包开发者。
示例:
{
"replace": false, // must be present
"entries": [
// represents a loot modifier in data/examplemod/loot_modifiers/example_glm_1.json
"examplemod:example_glm_1",
"examplemod:example_glm_2"
// ...
]
}
修改器 JSON
此文件保存修改器的所有值,例如生效概率、要添加的物品等。应尽量避免硬编码,让数据包作者可以按需调整平衡。修改器至少必须包含两个字段,也可能包含其他字段:
type:修改器的注册名称。conditions:触发修改器所需的战利品表条件列表。- 根据所用 codec,可能还有其他必填或可选属性。
建议 GLM 的常见用途是只向特定战利品表添加额外战利品。可使用
neoforge:loot_table_id条件实现。
示例:
{
// This is the registry name of the loot modifier
"type": "examplemod:my_loot_modifier",
"conditions": [
// Loot table conditions here
],
// Extra properties specified by the codec
"field1": "somestring",
"field2": 10,
"field3": "minecraft:dirt"
}
IGlobalLootModifier 与 LootModifier
要实际修改战利品表,必须提供 IGlobalLootModifier 实现。大多数情况下应使用 LootModifier,它会处理条件等工作。先让自定义修改器类继承它:
// We cannot use a record because records cannot extend other classes.
public class MyLootModifier extends LootModifier {
// See below for how the codec works.
public static final MapCodec<MyLootModifier> CODEC = ...;
// Our extra properties.
private final String field1;
private final int field2;
private final Item field3;
// First constructor parameter is the list of conditions. The rest is our extra properties.
public MyLootModifier(LootItemCondition[] conditions, String field1, int field2, Item field3) {
super(conditions);
this.field1 = field1;
this.field2 = field2;
this.field3 = field3;
}
// Return our codec here.
@Override
public MapCodec<? extends IGlobalLootModifier> codec() {
return CODEC;
}
// This is where the magic happens. Use your extra properties here if needed.
// Parameters are the existing loot, and the loot context.
@Override
protected ObjectArrayList<ItemStack> doApply(ObjectArrayList<ItemStack> generatedLoot, LootContext context) {
// Add your items to generatedLoot here.
return generatedLoot;
}
}
说明 修改器返回的掉落物列表会按注册顺序传入其他修改器。因此,应预期已修改的战利品仍可能被另一个修改器继续修改。
修改器 codec
为了让游戏识别修改器,必须为其定义并注册一个 codec。沿用前面的三个字段,写法如下:
public static final MapCodec<MyLootModifier> CODEC = RecordCodecBuilder.mapCodec(inst ->
// LootModifier#codecStart adds the conditions field.
LootModifier.codecStart(inst).and(inst.group(
Codec.STRING.fieldOf("field1").forGetter(e -> e.field1),
Codec.INT.fieldOf("field2").forGetter(e -> e.field2),
BuiltInRegistries.ITEM.byNameCodec().fieldOf("field3").forGetter(e -> e.field3)
)).apply(inst, MyLootModifier::new)
);
然后将 codec 注册到注册表:
public static final DeferredRegister<MapCodec<? extends IGlobalLootModifier>> GLOBAL_LOOT_MODIFIER_SERIALIZERS =
DeferredRegister.create(NeoForgeRegistries.Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS, ExampleMod.MOD_ID);
public static final Supplier<MapCodec<MyLootModifier>> MY_LOOT_MODIFIER =
GLOBAL_LOOT_MODIFIER_SERIALIZERS.register("my_loot_modifier", () -> MyLootModifier.CODEC);
内置修改器
NeoForge 提供以下开箱即用的修改器:
neoforge:add_table
该修改器会抽取第二张战利品表,并将结果加入当前正在修改的战利品表结果中。
{
"type": "neoforge:add_table",
"conditions": [], // the required loot conditions
"table": "minecraft:chests/abandoned_mineshaft" // the second table to roll
}
数据生成
GLM 可以通过数据生成创建。继承 GlobalLootModifierProvider:
public class MyGlobalLootModifierProvider extends GlobalLootModifierProvider {
// Get the PackOutput from GatherDataEvent.
public MyGlobalLootModifierProvider(PackOutput output) {
super(output, ExampleMod.MOD_ID);
}
@Override
protected void start() {
// Call #add to add a new GLM. This also adds a corresponding entry in global_loot_modifiers.json.
add(
// The name of the modifier. This will be the file name.
"my_loot_modifier_instance",
// The loot modifier to add. For the sake of example, we add a weather loot condition.
new MyLootModifier(new LootItemCondition[] {
WeatherCheck.weather().setRaining(true).build()
}, "somestring", 10, Items.DIRT);
// A list of data load conditions. Note that these are unrelated to the loot conditions
// specified on the modifier itself. For the sake of example, we add a mod loaded condition.
// An overload of #add is available that accepts a vararg of conditions instead of a list.
List.of(new ModLoadedCondition("create"))
);
}
}
与所有数据提供器一样,必须在 GatherDataEvent 中注册:
@SubscribeEvent // on the mod event bus
public static void onGatherData(GatherDataEvent event) {
event.getGenerator().addProvider(event.includeServer(), MyGlobalLootModifierProvider::new);
}
来源: NeoForged/Documentation(1.21.1)。中文翻译依据提交
4a680cdd4d15。