配方系统
EmakiCooking 的配方按工位分类存放在 plugins/EmakiCooking/recipes/<station>/。每类工位的交互不同,所以字段也不同:砧板按输入数量与切割次数,炒锅按翻炒与火候,烤炉按烘烤阶段,榨汁机按流体容量,发酵桶按时间阶段。
配方目录
| 目录 | 工位 | 典型玩法 |
|---|---|---|
recipes/chopping_board/ | 砧板 | 单输入,一次性接收主手整叠,按输入数量与切割次数产出切片或半成品。 |
recipes/wok/ | 炒锅 | 多输入,按火候、翻炒次数和容错决定结果。 |
recipes/grinder/ | 研磨机 | 输入材料,等待研磨时间后产出粉末。 |
recipes/steamer/ | 蒸锅 | 输入材料,按蒸制时间或链式步骤产出熟制品。 |
recipes/oven/ | 烤炉 | 单输入,按烘烤时间、完美火力比例和过烤阶段决定结果。 |
recipes/juicer/ | 榨汁机 | 单输入,多次压榨生成流体,再按容器容量盛取。 |
recipes/fermentation_barrel/ | 发酵桶 | 多输入,按发酵时间、提前收取和过度发酵决定结果。 |
当前推荐写法
当前默认资源统一使用 CoreLib Item Source 输入列表、单值产出字段和结果分支结构:
result:
success:
outputs:
- item_source: "minecraft-baked_potato"
amount: 1
actions:
- 'send_message text="<gold>料理完成。</gold>"'关键约定:
- 输入和输出优先写
item_sources列表,例如minecraft-carrot、minecraft-glass_bottle。 result.<branch>.outputs永远是列表,只有一个产物也写列表。result.<branch>.actions与outputs同级,位于同一个结果分支下。- 不再使用旧示例里的
output、result.output、result.outputs、result.actions、perfect_output、overbaked_output、fermentation.early_collect.output、fermentation.over_output。
通用字段
| 字段 | 必填 | 说明 |
|---|---|---|
id | 是 | 配方唯一 ID,建议与文件名一致。 |
display_name | 是 | 展示名称。支持 MiniMessage。 |
permission | 否 | 使用该配方所需权限;为空表示不限制。 |
condition | 否 | 完成或收取时评估的条件块,可配置 on_pass.actions、on_fail.actions 和 on_fail.block_output。 |
availability_condition | 否 | 配方是否可用(能否被匹配到)的条件块。与 condition 不同:它在匹配阶段评估,不通过则该配方直接不参与匹配。 |
structured_presentation | 否 | 配方级结构化展示配置。产物条目下也可写一份 outputs[].structured_presentation,两层会合并,产物级覆盖配方级。 |
result.<branch>.outputs | 是 | 当前分支产物列表。 |
result.<branch>.outputs[].chance | 否 | 该产物的掉落概率,取值 0~100,默认 100。<=0 恒不发放,>=100 恒发放。逐个产物独立判定。 |
result.<branch>.outputs[].amount_range | 否 | 产物数量随机区间,写 min / max 两个子键。存在时覆盖 amount;min > max 会自动交换。 |
result.<branch>.actions | 否 | 当前分支产出时执行的动作列表。 |
通用物品匹配器(matcher)
Cooking 的输入侧字段都支持 matcher,按组件、PDC、Lore 等条件进一步收窄物品判定。完整语法(类型全集、operator、path、嵌套写法)见 物品匹配器。
item_sources 与 matcher 是同级并行字段
所有输入判定位都由这两个字段合成,取 AND:
- 只写
item_sources:按物品源判定,不加额外条件。本页大量裸item_sources示例都是这种形态。 - 只写
matcher:只按组件 / PDC / Lore 等条件判定,不限来源。 - 两者都写:必须同时成立。
- 两者都不写:该条目永不命中(配方会被跳过)。
matcher 内部不能写物品源条件。 type: item_source / item_sources / source / sources 会被解析器拒绝并告警,该 matcher 恒不匹配。物品源一律写到同级的 item_sources。
工位角色侧(工具、锅铲、工位容器、加水规则输入)现在是嵌套子节点,每个子节点下同样是标准的 item_sources + matcher:stations.chopping_board.tool、stations.wok.spatula、stations.juicer.container、moisture_rules[].input。旧的扁平前缀键(tool_matcher、spatula_matcher、container_matcher、input_matcher)仍被加载期兼容读取,但新配置请写嵌套子节点。详见 工位系统。
| 配置位置 | 说明 |
|---|---|
ingredients[] | 配方原料。 |
inputs[] | 多输入配方的输入项。 |
input | 单输入配方的输入项。 |
container | 容器物品(如瓶子)。 |
stations.chopping_board.tool | 工具识别(config.yml)。 |
stations.wok.spatula | 锅铲识别(config.yml)。 |
stations.juicer.container | 工位容器识别(config.yml)。 |
stations.oven.fuels[] | 烤炉燃料(config.yml)。 |
stations.steamer.fuels[] | 蒸锅燃料(config.yml)。 |
stations.steamer.moisture_rules[].input | 蒸锅湿度规则输入(config.yml)。 |
nutrition.food_sources[] | 食物到营养的映射(config.yml,见营养系统)。 |
id: fresh_carrot_slice
ingredients:
# 「是胡萝卜」写 item_sources,「没被重命名过」写 matcher,两者 AND
- item_source: "minecraft-golden_carrot"
amount: 1
result:
success:
outputs:
- item_source: "minecraft-golden_carrot"
amount: 1DANGER
方块侧与产出侧不支持 matcher。
- 工位方块识别(
block_item_sources一类字段)是"哪个方块算这个工位",不是输入匹配。 - 产出侧(
result.<branch>.outputs[].item_sources)是"生成什么物品",属于构造而非匹配。
这两处写 matcher 不会生效。详见 只有输入匹配点支持 matcher。
结果分支
| 工位 | 常用结果分支 |
|---|---|
| 砧板 | success |
| 研磨机 | success |
| 蒸锅 | success,链式配方可按步骤继续处理。 |
| 炒锅 | success、undercooked、overcooked、invalid |
| 烤炉 | success、perfect、overbaked |
| 榨汁机 | success |
| 发酵桶 | success、early、over |
condition.on_fail.block_output 为 true 时,条件不满足会阻止产物输出;为 false 时仍可输出,但会执行失败动作。发酵桶在自动完成或破坏掉落等玩家可能离线的场景下,条件处理以源码运行时逻辑为准。
砧板示例
id: "cut_carrot"
display_name: "切制胡萝卜"
input:
item_sources:
- "minecraft-carrot"
amount: 2
cuts_required: 1
tool_damage: 1
result:
success:
outputs:
- item_source: "minecraft-golden_carrot"
amount: 1
actions:
- 'send_message text="<green>砧板处理完成。</green>"'| 字段 | 说明 |
|---|---|
input.item_sources | 必填(与 input.matcher 二者至少写一个)可匹配的输入来源。 |
input.amount | 每次完成切割需要并消耗的输入数量;未配置时按 1 处理。 |
cuts_required | 必填。 完成一轮切割所需点击次数。 |
tool_damage | 每次切割对工具造成的耐久损耗。 |
damage_override.chance | 覆盖 config.yml > stations.chopping_board.cut_damage.chance,即本配方切割时扣耐久的概率。 |
damage_override.value | 覆盖 config.yml > stations.chopping_board.cut_damage.value,即本配方每次扣除的耐久值。 |
damage_override 是可选段;未写时使用 config.yml 中的全局 cut_damage 设置。
砧板放入输入时会一次性接收玩家主手整叠,并在工位状态中累计数量;展示实体仍只显示 1 个物品。累计数量不足 input.amount 时不能开始切割。若设置 cuts_required: 1,玩家可以对已放入的一批食材连续完成多轮切割,无需每次重新放入 1 个输入。
炒锅示例
id: "example_recipe"
display_name: "简易炖菜"
ingredients:
- item_sources:
- "minecraft-carrot"
amount: 1
stir_rule: "1-3"
- item_sources:
- "minecraft-cooked_chicken"
amount: 1
stir_rule: "2-3"
heat_level: 1
stir_total:
min: 2
max: 5
fault_tolerance: 0
permission: "emakicooking.recipe.simple_stew"
condition:
type: all_of
entries:
- "%player_level% >= 5"
on_pass:
actions:
- 'send_message text="<green>厨艺达标,出锅成功。</green>"'
on_fail:
actions:
- 'send_message text="<red>厨艺等级不足,出锅失败。</red>"'
block_output: true
result:
success:
outputs:
- item_source: "minecraft-rabbit_stew"
amount: 1
actions:
- 'send_message text="<green>成功出锅。</green>"'
undercooked:
outputs:
- item_source: "minecraft-mushroom_stew"
amount: 1
actions: []
overcooked:
outputs:
- item_source: "minecraft-dried_kelp"
amount: 1
actions: []
invalid:
outputs:
- item_source: "minecraft-stone"
amount: 1
actions: []| 字段 | 说明 |
|---|---|
ingredients | 必填。 食材列表,缺失则该配方拒绝载入。 |
ingredients[].item_sources | 可匹配的食材来源。 |
ingredients[].amount | 需要数量。 |
ingredients[].stir_rule | 建议加入或翻炒区间,例如 1-3。 |
heat_level | 必填。 配方要求的火力等级。 |
stir_total.min/max | 两个子键都必填。 有效总翻炒次数范围。 |
fault_tolerance | 必填。 允许错误次数。 |
研磨机示例
id: "bone_meal"
display_name: "骨粉研磨"
input:
item_sources:
- "minecraft-bone"
grind_time_seconds: 6
permission: "emakicooking.recipe.bone_meal"
result:
success:
outputs:
- item_source: "minecraft-bone_meal"
amount: 3
actions:
- 'send_message text="<gray>研磨结束。</gray>"'| 字段 | 说明 |
|---|---|
input.item_sources | 必填(与 input.matcher 二者至少写一个)可匹配的输入来源。 |
grind_time_seconds | 必填。 研磨所需秒数。研磨机按 config.yml > stations.grinder.check_delay_ticks 周期推进。 |
蒸锅示例
id: "steamed_cod"
display_name: "清蒸鳕鱼"
input:
item_sources:
- "minecraft-cod"
required_steam: 40
permission: "emakicooking.recipe.steamed_cod"
result:
success:
outputs:
- item_source: "minecraft-cooked_cod"
amount: 1
actions:
- 'send_message text="<aqua>蒸制完成。</aqua>"'| 字段 | 说明 |
|---|---|
input.item_sources | 必填(与 input.matcher 二者至少写一个)可匹配的输入来源。 |
required_steam | 必填。 完成该配方需要累计消耗的蒸汽量。 |
requires_previous_step | 可选。填写另一个蒸锅配方 ID,表示本配方是该配方的后续步骤,用于链式蒸制。 |
链式示例:recipes/steamer/chain_example_recipe.yml 用 requires_previous_step: "example_recipe" 把上一步产物接入下一步。
烤炉示例
id: "baked_potato"
display_name: "烤马铃薯"
input:
item_sources:
- "minecraft-potato"
bake_time_seconds: 20
baking:
perfect_heat:
min: 45
max: 60
perfect_required_ratio: 0.7
overbake_seconds: 10
permission: "emakicooking.recipe.baked_potato"
result:
success:
outputs:
- item_source: "minecraft-baked_potato"
amount: 1
actions:
- 'send_message text="<gold>烤制完成。</gold>"'
perfect:
outputs:
- item_source: "minecraft-golden_carrot"
amount: 1
overbaked:
outputs:
- item_source: "minecraft-charcoal"
amount: 1烤炉配方的必填字段是 id、display_name、input.item_sources(或 input.matcher)和 bake_time_seconds,缺任意一项该配方直接拒绝载入。baking 段为可选。
烤炉会根据烘烤时间、处于完美火力区间的比例、以及完成后继续加热时间决定结果。高价值料理建议给玩家清楚展示火力和进度,避免“看不懂为什么过烤”。
榨汁机示例
id: "apple_juice"
display_name: "苹果汁"
input:
item_sources:
- "minecraft-apple"
presses_required: 5
fluid:
id: "apple_juice"
display_name: "苹果汁"
amount_ml: 180
container:
item_sources:
- "minecraft-glass_bottle"
serving_ml: 250
permission: "emakicooking.recipe.apple_juice"
result:
success:
outputs:
- item_source: "minecraft-honey_bottle"
amount: 1
actions:
- 'send_message text="<aqua>榨汁完成。</aqua>"'榨汁机配方的必填字段是 id、display_name、input.item_sources(或 input.matcher)和 presses_required,缺任意一项该配方直接拒绝载入。
一次压榨会向工位加入 fluid.amount_ml 的流体;盛取时消耗 container.serving_ml。例如默认苹果汁每次压榨 180ml,玻璃瓶盛取需要 250ml,所以玩家至少需要压榨两轮才可盛取一次。
发酵桶示例
id: "example_recipe"
display_name: "苹果酒"
inputs:
- item_sources:
- "minecraft-apple"
amount: 3
- item_sources:
- "minecraft-sugar"
amount: 1
fermentation_time_seconds: 300
fermentation:
early_collect:
min_progress_ratio: 0.5
over_time_seconds: 600
permission: "emakicooking.recipe.apple_cider"
condition:
type: all_of
entries:
- "%player_level% >= 3"
on_pass:
actions:
- 'send_message text="<green>发酵成熟。</green>"'
on_fail:
actions:
- 'send_message text="<yellow>发酵未满足额外条件。</yellow>"'
block_output: false
result:
success:
outputs:
- item_source: "minecraft-honey_bottle"
amount: 1
actions:
- 'send_message text="<gold>发酵完成。</gold>"'
early:
outputs:
- item_source: "minecraft-potion"
amount: 1
actions:
- 'send_message text="<yellow>提前收取了半发酵苹果饮。</yellow>"'
over:
outputs:
- item_source: "minecraft-honey_bottle"
amount: 1
actions:
- 'send_message text="<gold>苹果酒已经继续转化为酸甜果醋。</gold>"'| 字段 | 说明 |
|---|---|
inputs | 必填。 发酵桶需要投入的多种材料;每项使用同级 item_sources + matcher,两者取 AND。 |
inputs[].slot_id | 持久化槽位身份。当前配方中必须非空且唯一;不要在改配方时复用到另一种材料。 |
inputs[].count_key | 数量聚合与消费身份。可让多个稳定槽位共享同一聚合键,但必须保持长期稳定。 |
inputs[].matcher | 可选。只写非物品源条件,并与同级 item_sources 取 AND。 |
fermentation_time_seconds | 必填。 正常完成发酵所需秒数。 |
fermentation.early_collect.min_progress_ratio | 允许提前收取的最低进度比例。 |
fermentation.over_time_seconds | 正常完成后继续放置多久进入过度发酵。 |
result.early.outputs | 提前收取产物。产出项使用单值 item_source。 |
result.over.outputs | 过度发酵产物。产出项使用单值 item_source。 |
result.success.outputs | 正常完成产物。产出项使用单值 item_source。 |
调试与验证
- 使用
/ecooking reload重载配方。 - 使用
/ecooking inspect hand检查手持物能否被 Item Source 识别。 - 每个配方至少测试:权限不足、材料不足、背包满、正确分支、失败分支、服务器重启后的工位状态。
- 炒锅额外测试
success、undercooked、overcooked、invalid。 - 烤炉额外测试普通完成、完美完成、过烤。
- 榨汁机额外测试流体不足、容器不匹配和不同流体混合。
- 发酵桶额外测试提前收取、正常完成和过度发酵。