进度
进度是玩家可以完成的任务,可能推动游戏进程。玩家直接参与的任何操作都可以触发进度。
原创说明图;进度条件的逻辑依据 Minecraft Wiki 修正 Forge 原文的表述。
原版进度均通过 JSON 数据驱动,因此创建新进度不需要 Mod,只需要数据包。如何创建进度并放入 Mod 的 resources,参见 Minecraft Wiki。此外,进度可根据现有信息(如 Mod 是否加载、物品是否存在)条件加载并使用默认值。
进度条件
解锁进度需要满足指定条件。触发器跟踪条件,在执行特定操作时运行,例如击杀实体、改变物品栏或繁殖动物。进度加载到游戏时,定义的条件会被读取并作为监听器添加到触发器。随后调用触发函数(通常名为 #trigger),检查所有监听器,判断当前状态是否符合进度条件。只有完成所有要求获得进度后,监听器才会移除。
要求定义为字符串数组的数组,字符串表示进度中指定的条件名称。每个子数组中至少一个条件必须满足,并且所有子数组都必须满足。以下示例需要满足条件 1 或 2,且同时满足条件 3 或 4:
带注释和省略号的示例用于说明结构;实际 JSON 文件需移除注释并补全内容。
// 在某个进度 JSON 中
// 定义要满足的条件
"criteria": {
"example_criterion1": { /*...*/ },
"example_criterion2": { /*...*/ },
"example_criterion3": { /*...*/ },
"example_criterion4": { /*...*/ }
},
// 条件 1 或 2 成立,且条件 3 或 4 成立
"requirements": [
["example_criterion1", "example_criterion2"],
["example_criterion3", "example_criterion4"]
]
译注:Forge 原文将此例的 AND / OR 关系写反。此处按 Minecraft Wiki 的进度 JSON 格式说明更正。
原版触发器列表位于 CriteriaTriggers,JSON 格式参见 Minecraft Wiki。
自定义条件触发器
为自定义 AbstractCriterionTriggerInstance 子类实现 SimpleCriterionTrigger,即可创建自定义触发器。
AbstractCriterionTriggerInstance 子类
AbstractCriterionTriggerInstance 表示 criteria 对象中定义的单个条件。触发器实例负责保存定义的条件、返回输入是否匹配,并将实例写入 JSON 供数据生成使用。
条件通常通过构造函数传入。父类构造函数要求提供触发器注册表名称,以及玩家必须满足的 ContextAwarePredicate 条件。触发器名称应直接提供给父类,玩家条件应作为构造函数参数。
// ID 是触发器的注册表名称
public ExampleTriggerInstance(ContextAwarePredicate player, ItemPredicate item) {
super(ID, player);
// 保存必须满足的物品条件
}
触发器实例通常有静态工厂方法,方便数据生成时创建,也可以静态导入这些方法而不是类本身。
public static ExampleTriggerInstance instance(ContextAwarePredicate player, ItemPredicate item) {
return new ExampleTriggerInstance(player, item);
}
还应重写 #serializeToJson,将实例条件添加到其他 JSON 数据:
@Override
public JsonObject serializeToJson(SerializationContext context) {
JsonObject obj = super.serializeToJson(context);
// 将条件写入 JSON
return obj;
}
最后添加接收当前数据状态并返回用户是否满足必要条件的方法。玩家条件已通过 SimpleCriterionTrigger#trigger(ServerPlayer, Predicate) 检查。多数触发器实例将此方法命名为 #matches。
// 每种实例的方法各不相同,因此不是重写方法
public boolean matches(ItemStack stack) {
// ItemPredicate 匹配物品堆,因此输入是堆
return this.item.matches(stack);
}
SimpleCriterionTrigger
SimpleCriterionTrigger<T> 子类(T 为触发器实例类型)负责指定触发器注册表名称、创建实例,以及提供检查实例并在成功时运行监听器的方法。
#getId 提供的注册表名称应与触发器实例中的名称一致。通过 #createInstance 从 JSON 读取条件并创建实例:
@Override
public ExampleTriggerInstance createInstance(JsonObject json, ContextAwarePredicate player, DeserializationContext context) {
// 从 JSON 读取条件:item
return new ExampleTriggerInstance(player, item);
}
最后定义方法,检查所有实例并在条件满足时运行监听器。方法接收 ServerPlayer 及 AbstractCriterionTriggerInstance 子类的匹配方法定义的其他数据。内部应调用 SimpleCriterionTrigger#trigger 以正确检查所有监听器。多数触发器将其命名为 #trigger。
// 每种触发器的方法各不相同,因此不是重写方法
public void trigger(ServerPlayer player, ItemStack stack) {
this.trigger(player,
// AbstractCriterionTriggerInstance 子类中的条件检查方法
triggerInstance -> triggerInstance.matches(stack)
);
}
随后在 FMLCommonSetupEvent 期间使用 CriteriaTriggers#register 注册实例。
重要:
CriteriaTriggers#register不是线程安全的,必须通过FMLCommonSetupEvent#enqueueWork加入同步工作队列。
调用触发器
每当执行所检查的操作时,应调用 SimpleCriterionTrigger 子类定义的 #trigger:
// 在执行操作的代码中;EXAMPLE_CRITERIA_TRIGGER 是自定义条件触发器
public void performExampleAction(ServerPlayer player, ItemStack stack) {
// 执行操作的代码
EXAMPLE_CRITERIA_TRIGGER.trigger(player, stack);
}
进度奖励
完成进度时可发放奖励,包括经验值、战利品表、配方书配方,或以创造模式玩家身份执行的函数,也可以组合使用:
// 在某个进度 JSON 中
"rewards": {
"experience": 10,
"loot": [
"minecraft:example_loot_table",
"minecraft:example_loot_table2" // ...
],
"recipes": [
"minecraft:example_recipe",
"minecraft:example_recipe2" // ...
],
"function": "minecraft:example_function"
}
原文:Advancements。