Skip to content

强化配方 ​

一件装备的强化规则写在 recipes/*.yml 里: 各星级成功率, 需要什么材料, 失败怎么罚, 成功后加什么效果和动作

配方文件放在 recipes/*.yml. 配方的身份看必填的 id 字段, 与文件名无关; 文件名只用来组织文件和定位报错

顶层字段 ​

字段类型必填默认值取值范围说明
schema_versioninteger否11~2 (超出范围整份配方被跳过)材料的结构版本; 材料条目按这个版本做规范化
idstring是——配方唯一 ID; 缺失或为空时整份配方被跳过
display_namestring否取 id—配方显示名 (MiniMessage 格式)
gui_templatestring否strengthen_gui—GUI 模板引用 (gui/ 下模板名, 不含 .yml)
economyobject否enabled 依 currencies 是否为空—经济系统配置 (每次强化要扣的费用)
limitsobject否Limits.defaults()—强化限制 (最大星级, 锻印等), 见强化限制
success_ratesmap否空 (回退到 config.yml 全局值)键为目标星级正整数各星级基础成功率 (百分比)
matcherobject否无CoreLib 通用匹配器按非物品源条件判定哪些装备可使用此配方. 见匹配规则
source_patternslist否空 (不限来源)正则字符串按物品来源模式匹配 (忽略大小写的部分匹配), 与其余三个字段要同时满足. 见匹配规则
slot_groupslist否空 (不限槽位组)weapon / armor / offhand / generic槽位组匹配 (小写), 与其余三个字段要同时满足. 见匹配规则
stats_anylist否空 (不限属性)EmakiAttribute 属性 ID拥有其中任意一个属性就算满足, 与其余三个字段要同时满足
matchobject否无—已废弃, 不再被读取. 见从 match 迁移
stat_linesmap否空键为属性 ID; 子键见下属性行模板定义
name_actionslist否无名称操作条目全局名称修改操作
lore_actionslist否无Lore 操作条目全局 Lore 修改操作
starsmap否空键为目标星级正整数 (≤0 跳过)各星级阶段定义, 见星级阶段定义
branch_treeobject否无 (线性配方)—分支树定义. 详见分支树系统
conditionobject否无CoreLib ConditionBlock使用该配方的条件组

stat_lines.<属性 ID> 的子键:

字段类型必填默认值取值范围说明
templatestring否""—属性行模板; 留空时使用内置默认行
section_idstring否""—该属性行归属的展示区段 ID; 为空时归入 strengthen.stats.secondary
section_orderinteger否0≥ 0区段的排序权重; 填 0 时按区段 ID 是不是 primary 来推断

经济配置 ​

yaml
economy:
  enabled: true
  currencies:
    - provider: "vault"
      currency_id: ""
      base_cost: 320
      cost_formula: "%base_cost% * %star%"
      display_name: "<gold>金币</gold>"

cost_formula 支持表达式, 你可以引用 %base_cost%, %star% 等变量

字段类型必填默认值取值范围说明
enabledboolean否currencies 非空时为 truetrue / false是否启用货币费用; 关闭时不扣除任何货币
currencieslist<map>否[]—货币条目列表, 逐项见下
currencies[].providerstring否""经济提供者 ID经济提供者标识 (如 vault)
currencies[].currency_idstring否""—提供者内的货币 ID; 留空由提供者决定
currencies[].base_costlong否0≥ 0 (负值按 0)基础费用, 供 cost_formula 引用
currencies[].cost_formulastring否%base_cost%表达式实际费用表达式, 可引用 %base_cost%, %star% 等
currencies[].display_namestring否""—费用展示名

下面的示例按星级线性递增费用 (%star% 为目标星级):

yaml
economy:
  enabled: true
  currencies:
    - provider: "vault"
      currency_id: ""
      base_cost: 100
      cost_formula: "%base_cost% * %star%"
      display_name: "<gold>金币</gold>"

强化限制 ​

yaml
limits:
  max_star: 12
  max_temper: 4
  temper_chance_bonus_per_level: 5.0
  success_chance_cap: 90.0
字段类型必填默认值取值范围说明
max_starinteger否12≥ 1 (小于 1 按 1)最大可强化星级; 达到后无法继续强化
max_temperinteger否4≥ 0 (负值按 0)最大锻印等级 (失败时累积, 提升下次成功率)
temper_chance_bonus_per_leveldouble否5.0≥ 0 (负值按 0)每级锻印提供的成功率加成 (百分点)
success_chance_capdouble否90.00~100 (超出钳制)成功率上限 (含锻印加成后不超过此值)

每个星级还可以用 economy_override.currencies 覆盖该星级的费用 (结构与顶层 economy.currencies 相同)

匹配规则 ​

目标装备的匹配由文件顶层的四个字段决定, 四个都要满足:

yaml
slot_groups:
  - "weapon"

source_patterns:
  - "^emakiitem-example_item$"
字段类型说明
source_patternslist顶层的物品源条件, 按物品来源模式匹配 (正则)
matcherobject通用物品匹配器, 只按组件, PDC, Lore, 变量等非物品源条件判定. 语法见 物品匹配器
slot_groupslist按装备大类分的槽位组 (weapon/armor/offhand/generic), 不是 main_hand/helmet 这类具体装备槽位
stats_anylist拥有指定属性之一

顶层物品源条件走 source_patterns

配方顶层没有 item_sources 字段, 物品源条件一律写既有的 source_patterns. 它是正则, 忽略大小写的部分匹配, 所以想精确绑定单一物品源, 就在两端加上 ^ 和 $ 这两个首尾定位符: "^emakiitem-example_item$". 不加定位符的 "emakiitem-example_item" 会把 emakiitem-example_item_v2 这类来源也一并命中

matcher 内部不能写物品源条件 (type: item_source / item_sources / source / sources 会被拒绝并告警)

NOTE

想要精确绑定物品 ID, 就给 source_patterns 加上首尾定位符, 免得配方泛匹配到所有武器

从 match 迁移 ​

旧版的 match 块已不再被读取. 字段去向如下:

旧字段现在的写法
match.source_ids顶层 source_patterns, 每个 ID 写成两端带 ^ / $ 的正则 ("^minecraft-diamond_sword$")
match.lore_containsmatcher 里的 type: component + component: lore + operator: contains, 每条文本一个子条件
match.slot_groups提升为顶层 slot_groups
match.stats_any提升为顶层 stats_any
match.source_patterns提升为顶层 source_patterns
match.source_types无法自动转换, 需手工改写为 source_patterns 或 matcher 条件

/emakistrengthen convert-legacy 可以自动完成转换:

命令行为
/emakistrengthen convert-legacy预演, 只打印将要产生的差异, 不改文件
/emakistrengthen convert-legacy confirm实际改写 recipes/*.yml, 并为每个文件留一份 .bak 备份

--apply 与 confirm 等价, 命令需要 emakistrengthen.admin 权限

WARNING

含 match.source_types 的文件会被判为无法转换并跳过; 文件里同时存在 match 和已写好的 matcher (或对应顶层字段) 时会报冲突并跳过. 两种情况都需要手工处理

match.source_ids 会被自动转成两端带 ^ / $ 的正则并合并进顶层 source_patterns, 不会写进 matcher, 转换结果无需手工修正. 转换出的正则是精确匹配: 两端补上了 ^ / $ 定位符, ID 里的特殊字符也做了转义 (形如 ^\Qemakiitem-xxx\E$)

旧材料字段的规范化与拒绝条件 ​

解析阶段材料时, 旧的标量 item 会被改写成 item_sources, 并按 material_id → id → count_key → 单一来源 → 阶段索引的顺序补出 material_id; 省略 count_key 时回退到 material_id. 如果没写身份却给了多个来源, 解析器没法安全地选出唯一身份, 就会拒绝这条材料, 且不改写原文件里的映射. 注意: 上面的行为目前只由当前 JVM 的解析代码和本地测试证明, 还没在 Paper/Folia 实机上验证旧配置或旧存档的运行结果

通用物品匹配器 (matcher) ​

配方里有两处 matcher: 顶层那个判定目标装备, materials[].matcher 判定强化材料. 语法一致, 但物品源条件摆放的位置不同:

位置物品源条件写在哪与 matcher 的关系
顶层 matcher顶层 source_patterns (正则)顶层 source_patterns, slot_groups, stats_any, matcher 四者都要成立才会命中
materials[].matcher同级 item_sources两者一起交给 ItemRequirement.test 判定, 取且: 来源条件和非物品源条件都必须成立; 任何一个不成立, 就不算这份材料

NOTE

enhancement_recipes/*.yml 的 materials[] 同样是 item_sources + matcher 同级取 AND. 过去写在 matcher 内部的物品源条件请移到同级 item_sources

完整说明见 物品匹配器

yaml
id: enchanted_sword_strengthen

# 顶层: 只有附了锋利 3 以上的钻石剑能用这条配方
# 物品源走 source_patterns (带锚点精确绑定), matcher 只写组件条件
source_patterns:
  - "^minecraft-diamond_sword$"
matcher:
  type: component
  component: enchantments
  path: sharpness
  operator: '>='
  value: 3

stars:
  1:
    materials:
      # 材料: 来源和 matcher 条件统一判定, 必须同时满足
      - item_sources:
          - minecraft-copper_ingot
        matcher:
          type: component
          component: custom_name
          operator: absent
        amount: 3

WARNING

物品源用连字符 (minecraft-copper_ingot), 不是冒号. 写成 minecraft:copper_ingot 会被解析器判为无效并不报错地丢弃, 整个 item_sources 被丢空后等同于"不限来源". 详见 物品源用连字符, 组件 ID 用冒号

附魔强化配方的 target.filter ​

enhancement_recipes/*.yml 与 recipes/*.yml 是两套独立配方, 它的 target.filter 也认通用物品匹配器: filter 里带 type 键时按匹配器解析, 不带就按原有的 Provider 自由键值对解释

yaml
target:
  provider: equipment
  # 含 type 键 → 按通用物品匹配器解析
  filter:
    type: component
    component: enchantments
    operator: exists
yaml
target:
  provider: equipment
  # 不含 type 键 → 仍按 Provider 自由键值对解释 (原有行为)
  filter:
    item_type: SWORD

可引用的锻造品质变量 ​

target.filter 的 variable.<变量名> 键, target 的 matcher, 以及配方 conditions, 都能引用从目标物品 PDC 读到的锻造品质变量. 这些变量来自 EmakiForge 写在物品上的 emakiforge 命名空间 PDC

变量类型物品无锻造数据时说明
forge_quality_idstring""品质档位标识
forge_quality_displaystring""品质显示名
forge_quality_multiplierdouble1.0品质倍率
forge_recipe_idstring""锻造配方 ID

每个变量都有三种等价写法, 任选一种:

规范名点号写法 (历史兼容)简短写法
forge_quality_idforge.quality_idquality_id
forge_quality_displayforge.quality_displayquality_display
forge_quality_multiplierforge.quality_multiplierquality_multiplier
forge_recipe_idforge.forge_recipe_id无

点号写法为何保留

Forge 的 PDC 键已从 forge.quality_id 改名为 forge_quality_id (见 PDC 键名规范). 点号写法作为历史兼容别名保留, 已写好的配置不用改. 新配置建议直接用规范名

yaml
target:
  provider: equipment
  filter:
    # filter 是相等判定: 只命中品质档位恰为 legendary 的物品
    variable.forge_quality_id: legendary

filter 只做相等判定

filter 里的每一项都是相等比较, 不支持 >=, < 这类比较符. 数值按数值比, 其余按字符串忽略大小写比. 写 variable.forge_quality_multiplier: '>= 1.5' 不会报错, 但会拿字符串 ">= 1.5" 去和实际倍率比, 永远不命中

需要数值区间判断时, 改用 target 的 matcher (type: variable_expr) 或配方 conditions

还有一个辅助变量 forge_quality_multiplier_valid: 物品上的倍率能正常解析成有限正数时是 1, 写坏了 (非数值, 负数, NaN) 时是 0, 同时 forge_quality_multiplier 回退为 1.0. 用它就能区分"这件物品没锻造过"和"锻造数据坏了"

filter 中引用不存在的键会导致整条规则不命中

filter 里写了既不是内置键, 也不在变量表里的键名时, 该条过滤直接判为不匹配. 上表四个变量始终存在 (无锻造数据时取默认值), 因此可以安全引用

NOTE

enhancement_recipes/ 的其余字段见通用强化配方

全局名称与 Lore 修改 ​

每次强化成功后, 这些操作都会套用到物品上, 支持 %star%, %temper%, %max_temper%, %temper_color% 等占位符

yaml
name_actions:
  - action: "append_suffix"
    value: " [+%star%]"

lore_actions:
  - action: "prepend"
    content:
      - "<gradient:#F2C46D:#C9703D>强化等级 +%star%</gradient>"
      - "<gray>锻印: %temper_color%%temper%/%max_temper%</gray>"

星级阶段定义 (stars) ​

每个星级可以写这些字段:

字段类型必填默认值取值范围说明
namestring否""—该星级的里程碑名称 (用于 GUI 展示)
effectslist<map>否[]效果条目, type 取值见效果类型效果列表 (统一格式)
materialslist<map>否[]材料条目见下强化所需材料
economy_overrideobject否currencies: []currencies 结构与顶层 economy 相同覆盖该星级的经济费用; 填了内容时优先用这里的, 而不是顶层 economy
actionsobject否无键为 success / failure强化成功/失败时执行的动作

材料字段 ​

字段类型必填默认值取值范围说明
material_idstring否按 id, count_key, 单一来源或阶段索引回退—材料选择和阶段规则用的稳定身份. 新配置建议明确写上, 免得来源或配置形态一变, 身份也跟着漂移; 声明了多个来源又没给出身份时, 这条材料会被拒绝
count_keystring否material_id—数量聚合和消耗用的身份. 可以和 material_id 不一样; 想让多份材料共享计数时, 就明确填同一个值
item_sourceslist否空 (不限来源)物品源简写 (连字符, 如 minecraft-copper_ingot)允许的材料物品来源. 省略表示不限来源; 旧的标量 item 字段只在读取时改写成这个字段
matcherobject否无CoreLib 通用匹配器只写组件, PDC, Lore 等非物品源条件. 与同级 item_sources 经统一 ItemRequirement.test 取 AND; 两者都省略时这条材料永远不命中. 见 物品匹配器
amountinteger否1-1 或 ≥ 1需要数量. 设为 -1 表示不消耗, 仅检测是否持有
optionalboolean否falsetrue / false是否为可选材料 (不放也能强化)
protectionboolean否falsetrue / false放入后提供保护效果, 失败时不降星
temper_boostinteger否0≥ 0 (负值按 0)放入后额外提供的锻印等级, 用于提升本次成功率
yaml
materials:
  - material_id: "copper_ingot"
    count_key: "copper_ingot"
    item_sources:
      - "minecraft-copper_ingot"
    amount: 3
  # 保护材料: 不消耗, 仅检测持有
  - material_id: "protection_gold_nugget"
    count_key: "protection_gold_nugget"
    item_sources:
      - "minecraft-gold_nugget"
    amount: -1
    optional: true
    protection: true
  # 锻印材料: 额外提供 1 级锻印加成
  - item_sources:
      - "minecraft-potion"
    amount: -1
    optional: true
    temper_boost: 1

效果类型 ​

字段类型必填默认值取值范围说明
effects[].typestring是—variables / ea_attribute / es_skill / lore_action / name_action效果类型; 不在此列的条目被忽略
effects[].variablesmap否无属性 ID → 数值或表达式type: variables 时读取; 用于 Lore, 名称/Lore 模板与占位符上下文, 不会自动写入 Attribute PDC
effects[].ea_attributesmap否无属性 ID → 数值或表达式type: ea_attribute 时读取; 写入 EmakiAttribute PDC 属性层 (显式覆盖)
effects[].es_skillslist否无EmakiSkills 技能 IDtype: es_skill 时读取; 技能附加
effects[].lore_actionslist否无Lore 操作条目type: lore_action 时读取; 对物品 Lore 的修改
effects[].name_actionslist否无名称操作条目type: name_action 时读取; 对物品名称的修改

除效果列表外, 星级阶段还支持同级的直接键 variables, ea_attributes, name_action, lore_action: 它们与对应的 effects 条目是累加关系, 读取含义一致

当前效果的边界:

  • variables 只提供文本, 占位符和动作模板上下文, 不会自动成为 Attribute PDC 属性
  • 想要真实属性输出, 必须显式配置 type: "ea_attribute" 和 ea_attributes
  • 技能附加用 type: "es_skill", 但具体技能 ID 要写在 es_skills 列表里

Lore 操作策略 ​

强化配方的 Lore 操作按下面的策略走, 保证每种属性在 Lore 里始终只有一行:

场景操作说明
★1 首次添加action: "append"首次使用 append 添加属性行
★2+ 更新已有行action: "replace_line"通过 anchor 匹配已有行并替换为新数值
里程碑解锁新属性先 replace_line 再 append先更新旧行, 再 append 添加新属性行

星级示例 ​

yaml
stars:
  # ★1: 首次添加属性行
  1:
    effects:
      - type: "variables"
        variables:
          physical_attack: 4
      - type: "lore_action"
        lore_actions:
          - action: "append"
            content:
              - "<gray>物理伤害: <gold>+4</gold></gray>"
    materials:
      - item_sources:
          - "minecraft-copper_ingot"
        amount: 1
      - item_sources:
          - "minecraft-gunpowder"
        amount: 2

  # ★2: 使用 replace_line 更新已有行
  2:
    effects:
      - type: "variables"
        variables:
          physical_attack: 4
      - type: "lore_action"
        lore_actions:
          - action: "replace_line"
            anchor: "物理伤害:"
            content:
              - "<gray>物理伤害: <gold>+8</gold></gray>"
    materials:
      - item_sources:
          - "minecraft-copper_ingot"
        amount: 1

  # ★6 里程碑: 解锁新属性
  6:
    name: "觉醒"
    effects:
      - type: "variables"
        variables:
          physical_attack: 6
          physical_crit_rate: 3
      - type: "ea_attribute"
        ea_attributes:
          physical_attack: 28.0
          physical_crit_rate: 3.0
      - type: "es_skill"
        es_skills:
          - "weapon_aura"
      - type: "lore_action"
        lore_actions:
          # 先更新已有行
          - action: "replace_line"
            anchor: "物理伤害:"
            content:
              - "<gray>物理伤害: <gold>+28</gold></gray>"
          # 再添加新属性行
          - action: "append"
            content:
              - "<gray>暴击率: <gold>+3%</gold></gray>"
              - "<yellow>★ 觉醒: 解锁武器光环</yellow>"
    materials:
      - item_sources:
          - "minecraft-iron_ingot"
        amount: 3
    actions:
      success:
        - 'send_message text="<gold>★ 觉醒!装备提升至 +%star%!</gold>"'
        - "play_sound sound=minecraft:entity.player.levelup volume=1 pitch=1.5"
      failure:
        - 'send_message text="<red>强化失败,装备等级下降至 +%star%。</red>"'

分支配方 ​

强化配方支持分支树系统, 装备可以在特定星级分叉成不同的强化路线. 详见 分支树系统 文档

完整示例 ​

yaml
id: "weapon_physical"
display_name: "<gold>物理武器强化</gold>"
gui_template: "strengthen_gui"

economy:
  enabled: true
  currencies:
    - provider: "vault"
      currency_id: ""
      base_cost: 320
      cost_formula: "%base_cost% * %star%"
      display_name: "<gold>金币</gold>"

limits:
  max_star: 12
  max_temper: 4
  temper_chance_bonus_per_level: 5.0
  success_chance_cap: 90.0

success_rates:
  1: 100.0
  2: 100.0
  3: 95.0
  4: 85.0
  5: 75.0
  6: 60.0
  7: 45.0
  8: 32.0
  9: 22.0
  10: 14.0
  11: 8.0
  12: 4.0

slot_groups:
  - "weapon"

name_actions:
  - action: "append_suffix"
    value: " [+%star%]"

lore_actions:
  - action: "prepend"
    content:
      - "<gradient:#F2C46D:#C9703D>强化等级 +%star%</gradient>"
      - "<gray>锻印: %temper_color%%temper%/%max_temper%</gray>"

stars:
  1:
    effects:
      - type: "variables"
        variables:
          physical_attack: 4
      - type: "lore_action"
        lore_actions:
          - action: "append"
            content:
              - "<gray>物理伤害: <gold>+4</gold></gray>"
    materials:
      - item_sources:
          - "minecraft-copper_ingot"
        amount: 1
  2:
    effects:
      - type: "variables"
        variables:
          physical_attack: 4
      - type: "lore_action"
        lore_actions:
          - action: "replace_line"
            anchor: "物理伤害:"
            content:
              - "<gray>物理伤害: <gold>+8</gold></gray>"
    materials:
      - item_sources:
          - "minecraft-copper_ingot"
        amount: 2

enhancement_recipes 通用强化配方 ​

enhancement_recipes/*.yml 是通用强化框架 (target / materials / costs / chance / pity / actions) 的配方, 与 recipes/*.yml 是两套独立配方. 分工是: EmakiStrengthen 管材料匹配与消耗, 货币扣费, 成功率, 保底 (连续失败到一定次数后必定成功), 重试时结果不变和出错补偿; 目标识别与等级/阶段写回交给已注册的 Target Provider

顶层字段 ​

字段类型必填默认值取值范围说明
schema_versioninteger否11~2 (超出范围整份配方被跳过)材料的结构版本
idstring是——配方唯一 ID; 缺失或为空时整份配方被跳过
modestring是—equipment / affix / gem用途分组; 缺失, 为空或不在取值内时整份配方被跳过
targetobject是—见 target目标配置; provider 为空时整份配方被跳过
materialslist<map>否[]见 materials[]材料槽列表; 单个槽解析失败会使整份配方被跳过
costslist<map>否[]见 costs[]货币消耗列表; 单个条目解析失败会使整份配方被跳过
chancenumber | object否1.00~1 概率; 或 CoreLib Quantity (fixed / formula / lookup_table)成功率; 非法配置会使整份配方被跳过
pityobject否无见 pity单轨保底; 存在但非法 (缺 group 等) 会使整份配方被跳过
pity_trackslist<map>否[]见 pity多轨保底; 单条非法会使整份配方被跳过
conditionobject否无CoreLib ConditionBlock进入条件; 在 prepare() 内求值, 预览与正式强化同时生效
actionsmap否{}键名自由的 CoreLib 动作行列表结算后按结果阶段触发的动作分组 (如 on_success / on_failure)

target ​

字段类型必填默认值取值范围说明
providerstring是—已注册 Target Provider ID (内置 equipment / affix / gem)目标提供者; 为空时整份配方被跳过
filtermap否无含 type 键时按通用物品匹配器解析; 否则按 Provider 自由键值对目标过滤, 含义见附魔强化配方的 target.filter

materials[] ​

字段类型必填默认值取值范围说明
item_sourceslist否空 (不限来源)物品源简写与同级的 matcher 取 AND (同时满足); 两者都省略时这个槽会失败
matcherobject否无CoreLib 匹配器组件, PDC, Lore 等非物品源条件
quantitynumber | object否1CoreLib Quantity需求数量
consumestring否alwaysalways / success / failure / never消耗时机; 非法值按 always
requiredboolean否truetrue / falsefalse 时缺料不阻断 (等价 optional: true)
optionalboolean否无true / falserequired 的反向写法; 与 required 同时出现时 optional 生效
material_idstring否依次取 count_key, 来源身份, 条件身份—规则与选择身份
count_keystring否material_id—数量聚合和消耗用的身份
target_comparestring否空 (不做比较)same_affix / same_affix_set / same_item_type / same_level / level_at_least把该槽材料与当前目标比较; 非法值按不比较

costs[] ​

字段类型必填默认值取值范围说明
providerstring否""经济提供者 ID经济提供者标识
currency_idstring是——货币 ID; 可用别名 currency. 为空时该条目解析失败, 并因条目数不一致使整份配方被跳过
amountnumber | object否Quantity 默认CoreLib Quantity费用数额

pity / pity_tracks[] ​

字段类型必填默认值取值范围说明
scopestring否itemitem / player计数范围; 非法值按 item
groupstring是——计数器分组; 为空时整段 pity 被丢弃
thresholdinteger否10≥ 1 (≤0 使整段被丢弃)触发阈值; 与 formula 二选一
formulaobject否无CoreLib Quantity触发条件表达式; 与 threshold 二选一
effectstring否force_successforce_success / chance_bonus保底效果; 非法值按 force_success
bonus_valuedouble否无chance_bonus 时必填且 > 0chance_bonus 的加成值; 非有限值使整段被丢弃
decay.typestring否resetreset / fixed_decay / proportional计数衰减方式; 非法值按 reset, 值域越界使整段被丢弃
decay.valuedouble否0.0fixed_decay ≥ 0; proportional 0~1衰减量
isolatelist否空recipe / mode / target / affix / level把这条 track 的计数按维度分组存放; 写入未知或重复的维度会使这份配方加载失败

pity 与 pity_tracks 会合并成同一份轨道列表; scope + group 相同的轨道会被静默去重, 保留先出现的那条

下面是自带示例 (enhancement_recipes/example_enhancement_recipe.yml 节选):

yaml
schema_version: 2
id: example_equipment_enhance
mode: equipment

target:
  provider: equipment
  filter:
    item_type: SWORD

materials:
  - item_sources:
      - "minecraft-diamond"
    quantity: 2
    consume: always
  - matcher:
      type: pdc_match
      key: "emakiitem:item_type"
      value: "enhance_stone"
    quantity:
      type: formula
      expression: "1 + floor(target.level / 5)"
    consume: success

costs:
  - provider: vault
    currency_id: money
    amount:
      type: formula
      expression: "100 * (target.level + 1)"

chance:
  type: formula
  expression: "max(0.05, 0.9 - target.level * 0.05)"

pity:
  scope: item
  group: example_equipment_enhance
  threshold: 10
  effect: force_success
  decay:
    type: reset
    value: 0

actions:
  on_success:
    - "sound: ITEM_TOTEM_USE"
  on_failure:
    - "sound: BLOCK_ANVIL_USE"