简体中文
品质系统
品质等级怎么配置, 锻造结果落在哪个品质由什么决定, 保底怎么算, 以及品质怎么改变成品的名称和 Lore
品质系统决定锻造结果落在哪个品质等级, 同时影响材料效果的数值倍率和物品的展示
全局品质配置
品质在 config.yml 的 quality 节点里做全局配置. 发货默认值:
yaml
quality:
tiers:
- name: "平庸"
weight: 60
multiplier: 1.0
- name: "精良"
weight: 30
multiplier: 1.05
- name: "有缺"
weight: 10
multiplier: 0.95
- name: "优质"
weight: 5
multiplier: 1.1
- name: "无暇"
weight: 2
multiplier: 1.15
- name: "完美"
weight: 1
multiplier: 1.2
default_tier: "平庸"
guarantee:
enabled: true
threshold: 60
minimum: "无暇"
item_meta:
enabled: true
tiers:
无暇:
name_actions:
- action: "prepend_prefix"
value: "<color:#4B64C8>[无暇] </color>"
lore_actions:
action:
- 'broadcast_message text="<color:#4B64C8>%player_name%锻造出了一个无暇品质的%var.forge_show_item%</color>"'
完美:
name_actions:
- action: "prepend_prefix"
value: "<color:#FFD700>[完美] </color>"
lore_actions:
action:
- 'broadcast_message text="<color:#FFD700>%player_name%锻造出了一个完美品质的%var.forge_show_item%</color>"'
# 平庸 / 精良 / 有缺 / 优质 只有 name_actions, 无 action 广播config.yml 的 item_meta.tiers 共 6 个档位; 上例省略了其余 4 档, 它们与"无暇"结构相同, 但只有 name_actions
quality 字段
| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
quality.tiers | list<object> | 否 | 单一品质 普通 (权重 100, 倍率 1.0) | 每项为记录或 "品质名-权重-倍率" 字符串 | 品质池. 留空或全部条目都不合法时, 退到内置默认池, 同时把保底和 item_meta 一起关掉 |
quality.tiers[].name | string | 是 (该记录存在时) | — | 非空字符串 | 品质的唯一标识, 同时也是显示名. 留空时这一项被忽略 |
quality.tiers[].weight | integer | 是 (该记录存在时) | — | > 0 | 抽取权重, 越大越容易被抽到. <= 0 时这一项被忽略 |
quality.tiers[].multiplier | number | 否 | 1.0 | 任意数值 | 材料效果数值要乘的倍数 (配置值 × 材料数量 × 品质倍率) |
quality.default_tier | string | 否 | 普通 | 品质池中存在的品质名 | 没抽到有效品质名时用的备用品质. 写进去的名字不存在时, 改用品质池的第一项 |
quality.guarantee | object | 否 | 见下 | — | 保底配置, 见保底机制 |
quality.item_meta | object | 否 | 见下 | — | 品质物品元数据, 见 item_meta 品质写入 |
yaml
quality:
tiers:
- name: "普通"
weight: 70
multiplier: 1.0
- name: "精良"
weight: 25
multiplier: 1.05
- name: "史诗"
weight: 5
multiplier: 1.15
default_tier: "普通"tiers 格式
quality.tiers 的每一项既可以写成上面的记录形式, 也可以写成 "品质名-权重-倍率" 字符串 (用 - 切成三段); 两种写法都解析失败时, 这一项被忽略
NOTE
tiers 全部解析失败时, 会退回内置默认品质池 (只有一种品质 普通, 权重 100, 倍率 1.0), 同时保底和 item_meta 都会关掉
保底机制
下表"默认值"是指配置里没写这个键时代码采用的回退值, 和随包 config.yml 里的发布值不一样. 随包发布值是 enabled: true, threshold: 60, minimum: "无暇", 也就是随包配置本身就把保底打开了
| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
quality.guarantee.enabled | boolean | 否 | false | true / false | 是否打开全局保底. 随包发布值是 true |
quality.guarantee.threshold | integer | 否 | 10 | >= 1 (< 1 提升为 1) | 连续多少次没达到最低品质就触发保底. 随包发布值是 60 |
quality.guarantee.minimum | string | 否 | 普通 | 品质池中存在的品质名 | 保底触发时给的最低品质等级. 这个名字不存在时退回 default_tier. 随包发布值是 无暇 |
保底判定规则:
- 每次锻造后, 比较这次抽中的品质在品质池里的位次和保底最低品质的位次
- 位次更低时, 保底计数 +1; 否则把保底计数重置为 0
- 计数达到
threshold - 1后, 下一次锻造直接给出保底品质, 并把计数重置
yaml
quality:
guarantee:
enabled: true
threshold: 30
minimum: "精良"NOTE
保底计数器按"玩家 + 配方"分别记录, 存在 data/<玩家UUID>.yml 里. quality_modify 的 force 模式会跳过全部保底逻辑, 既不触发保底也不更新计数. 配方自己的保底 (attempts) 和全局保底只会生效其中一个, 见配方级别品质覆盖
item_meta 品质写入
quality.item_meta.enabled 为 true 时, 锻造成功后会按品质等级对结果物品多做几步操作:
| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
quality.item_meta.enabled | boolean | 否 | false | true / false | 是否把品质对应的名称, Lore 和动作写到锻造结果上. 随包发布值是 true |
quality.item_meta.tiers.<品质名> | object | 否 | 空 | 键为品质名, 大小写不敏感 | 按品质登记的元数据. 品质名不在注册表里时, 这一条不生效 |
quality.item_meta.tiers.<品质名>.name_actions | object | list | 否 | 不登记 | CoreLib 名称动作 | 对结果物品名称执行的动作. 值为 null (只写键, 不给值) 时不登记这个品质的名称动作 |
quality.item_meta.tiers.<品质名>.lore_actions | object | list | 否 | 不登记 | CoreLib Lore 动作 | 对结果物品 Lore 执行的动作. 随包 config.yml 在这里只写键, 不给值 (也就是 null), 含义是"不登记这个品质的 Lore 动作"; 这和显式写空列表 [] (登记一条空动作链) 不一样 |
quality.item_meta.tiers.<品质名>.action | list<string> | 否 | 空列表 | CoreLib 动作行 | 按品质执行的动作列表 (比如全服广播) |
下面是一个可复制的例子: 给"精良"加名称前缀, 并让"完美"触发全服广播
yaml
quality:
item_meta:
enabled: true
tiers:
精良:
name_actions:
- action: "prepend_prefix"
value: "<color:#1F871F>[精良] </color>"
完美:
name_actions:
- action: "prepend_prefix"
value: "<color:#FFD700>[完美] </color>"
action:
- 'broadcast_message text="<color:#FFD700>%player_name% 锻造出了完美品质的 %var.forge_show_item%</color>"'品质元数据的动作与配方 result.success 的动作, 材料效果动作会合并到一起, 统一应用到结果物品上
确定性算法
品质计算用的是确定性算法: 品质由一段指纹算出来, 指纹里包含"玩家 UUID + 配方 ID + 当前保底计数 + 目标与各槽位物品签名", 再加上预览种子, 不是每次现掷随机数. 内部对这段 roll key 取 SHA-256, 用哈希前 12 位十六进制字符换算成一个 0~1 的比例, 再拿这个比例去品质池上做加权选择
已经准备好的锻造结果会缓存下来, 最多 128 项, 每项存活 30 秒, 因此:
- 你在界面里看到的品质结果和实际锻造结果一致
- 30 秒内重复提交相同输入不会改变结果
- 你换了材料, 或等缓存过期之后, 结果会变化
配方级别品质覆盖
每个配方都能在自己的 quality 节点里覆盖全局配置. 注意配方里的字段名和全局配置不一样:
yaml
quality:
enabled: true
custom_pool:
- "普通-50-1.0"
- "精良-35-1.08"
- "史诗-15-1.25"
guarantee:
enabled: true
attempts: 5
minimum: "精良"| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
enabled | boolean | 否 | false | true / false | 是否打开配方自己的品质配置. 为 false 时 custom_pool 和配方自己的保底都不生效 |
custom_pool | list<string> | 否 | 空 | "品质名-权重-倍率" 字符串 | 这个配方专用的品质池. 为空或全部条目不合法时, 用全局的 quality.tiers |
guarantee.enabled | boolean | 否 | false | true / false | 是否打开配方自己的保底 |
guarantee.attempts | integer | 否 | 10 | >= 1 (< 1 提升为 1) | 配方自己的保底阈值. 对应全局配置里的 threshold |
guarantee.minimum | string | 否 | 普通 | 品质池中存在的品质名 | 配方保底给的最低品质. 这个名字不存在时取配方池的第一项, 还是没有就退回全局保底的最低品质 |
NOTE
配方自己的保底没打开, 而全局保底打开了时, 就退回全局的 threshold 与 minimum. 只有配方的 quality.enabled 和 quality.guarantee.enabled 同时为 true 时, attempts / minimum 才会被采用
与材料效果的交互
variables效果值 =配置值 × 材料数量 × 品质倍率ea_attribute效果值 =配置值 × 材料数量 × 品质倍率quality_modify的force模式会跳过保底判定和计数更新quality_modify的minimum模式只在抽中品质低于指定品质时, 把结果提升到该品质
材料效果的详细写法见材料系统