Skip to main content

自定义配方处理逻辑

这篇文档对应的使用场景是:

  • 默认的“输入价值求和”不适合你的配方类型
  • 你要处理多产物、自定义 RecipeType、额外工序成本
  • 你希望对配方生成值做全局修正

先明确默认行为的边界

模组默认的配方处理器做的事情很简单:

  1. 取配方输入。
  2. 计算输入价值总和。
  3. 取输出列表中的第一个物品。
  4. 把总价值赋给该输出,并按堆叠数量平摊。

所以一旦你的配方不满足这种“单产物、直接求和”的结构,就应该考虑自定义。

入口事件

配方扩展都在这个事件里完成:

OEVEvents.addRecipeHandler(event => {
})

场景一:给新的 RecipeType 启用默认处理

如果某个模组新加了一种配方类型,但它本质上仍然是普通输入求和,那最省事的做法就是直接注册默认处理器。

let $RecipeType = Java.loadClass('net.minecraft.world.item.crafting.RecipeType')

OEVEvents.addRecipeHandler(event => {
event.addSimpleRecipeHandler($RecipeType.BLASTING)
})

这适用于:

  • 只是默认列表里没包含的 RecipeType
  • 但其输入输出结构仍然普通

场景二:完全自定义处理器

当你需要自己控制输入、输出、额外成本和赋值规则时,使用 addCustomRecipeHandler

let $RecipeType = Java.loadClass('net.minecraft.world.item.crafting.RecipeType')

OEVEvents.addRecipeHandler(event => {
event.addCustomRecipeHandler(
$RecipeType.CRAFTING,
event.defaultRecipeInputGetter,
event.defaultRecipeOutputGetter,
event.defaultRecipeExtraValueGetter,
(recipe, stacks, totalValue, setter) => {
setter.set(recipe, stacks[0], totalValue * 10)
}
)
})

上面这个例子会把配方结果价值改成输入总成本的十倍。

虽然只是演示,但已经能说明五个参数分别负责什么。

五个参数怎么理解

1. RecipeType

指定你正在处理哪一种配方类型。

2. inputGetter

List<Ingredient> get(Recipe<?> recipe);

负责告诉模组“这个配方有哪些输入 Ingredient”。

如果配方本身结构正常,直接用默认的 event.defaultRecipeInputGetter 就够了。

3. outputGetter

List<ItemStack> get(Recipe<?> recipe, RegistryAccess registryAccess);

负责告诉模组“这个配方产出哪些物品”。

默认逻辑只会从标准结果位里取一个输出。

如果某类配方会同时产出多个物品,或者输出不在标准位置,就要自己改这里。

4. extraValueGetter

int get(Recipe<?> recipe);

负责添加“配方额外成本”。

这很适合处理下面这些情况:

  • 熔炼需要燃料
  • 某种工序有固定手续费
  • 配方要消耗某个逻辑上不写在输入里的额外资源

5. valueSetter

void accept(Recipe<?> recipe, List<ItemStack> stacks, int totalValue, IRecipeGenValueSetter setter);

负责把总价值真正分配给输出。

默认情况下只会给第一个输出设置值。

如果你有多输出,就必须自己决定怎么分配。

场景三:给某类工序增加统一成本

如果你只是想对生成后的结果统一修正,而不是整套重写处理逻辑,可以使用:

OEVEvents.addRecipeHandler(event => {
event.modifyRecipeGenValue((type, oldValue) => {
if (type === 'minecraft:smelting') return oldValue + 64
return oldValue
})
})

这个回调会在配方值写入前执行。

适合做:

  • 熔炼燃料成本
  • 机器加工税
  • 某类工序固定损耗
  • 整体倍率修正

场景四:处理特殊 Ingredient

有些模组会使用自定义 Ingredient 类,而默认 getItems() 的结果可能不完整,或者根本不代表真实输入范围。

此时可以注册特殊原料处理器:

OEVEvents.addRecipeHandler(event => {
event.register(SomeIngredientClass, ingredient => {
return 1024
})
})

这个接口属于高级联动能力,适合:

  • 你清楚目标模组 Ingredient 的实现
  • 你知道默认逻辑为什么算不对
  • 你希望给这种 Ingredient 单独定义取值规则

如何排查“配方类型到底叫什么”

模组提供了两个辅助方法:

OEVEvents.addRecipeHandler(event => {
console.log(event.getAllRecipeType())
console.log(event.getAllRecipeTypeName())
})

它们可以帮助你先把已注册的配方类型枚举出来,再决定对哪一种做处理。

多输出时的思路建议

如果一个配方会同时得到多个输出,最重要的问题不是“能不能算”,而是“怎么分才合理”。

常见策略有三种:

1. 主产物吃掉绝大多数价值

适合副产物只是顺带给一点补偿的场景。

2. 按你预设的比例拆分

适合你已经有明确设计意图的机器链。

3. 只给一个输出定值,剩下输出视为无价值副产物

适合你想尽量避免经济系统被副产物刷爆。

实战建议

当你开始写自定义处理器时,建议不要同时改很多件事。

更稳妥的顺序是:

  1. 先确认配方类型名。
  2. 先尝试 addSimpleRecipeHandler 看能否满足。
  3. 不够再改 addCustomRecipeHandler
  4. 最后再考虑特殊 Ingredient 处理。

这样排错成本最低。