Skip to content

付费扩容档位 ​

unlock_costs.yml 是 GUI 里付费扩容的价格档位. 它是独立的业务数据文件: release_default_data 只在文件缺失时释放一次, 版本升级不会覆盖你已经改过的内容

文件名由 config.yml 的 unlock.cost_file 指定, 默认就是 unlock_costs.yml; 玩家能不能购买, 由 unlock.purchase_enabled 与 emakistorage.unlock.purchase 权限共同决定, 见配置详解

unlock_costs.yml 结构 ​

yaml
tiers:
  - count_range: "1-9"
    currency:
      type: vault
      amount: 1000
  - count_range: "10-19"
    item:
      source: "diamond"
      amount: 16
  - count_range: "20-49"
    currency:
      type: vault
      amount: 50000
    item:
      source: "netherite_ingot"
      amount: 4

fallback:
  currency:
    type: vault
    amount: "100000 * 1.05 ^ (%count% - 50)"
  max_amount: 1000000000

batch:
  enabled: true
  options: [1, 5, 10, 64]
字段类型必填默认值取值范围说明
tierslist<map>否见示例 (3 档)每个元素为下表的键集付费档位的列表. 某一档的 count_range 不合法, 或者两种价格都没写, 这一档会被整档跳过并记 warning
tiers[].count_rangestring是— (无该键的档被跳过)"N" 或 "min-max"; 要求 min ≥ 1 且 max ≥ min这一档覆盖的"第几格到第几格"区间, 比如 1-9. 排在前面的档优先
tiers[].currencysection否发货档位部分含此项见下方三行用货币支付的代价. 和 item 至少要写一个, 否则这一档会被跳过
tiers[].currency.typeenum否vaultvault / excellent货币后端的 id, 填了不认识的值会退回 vault
tiers[].currency.currencystring否"" (空)后端自身的货币标识传给经济后端的货币 id; 留空表示用这个后端的默认货币. 发货文件里没有写这个键
tiers[].currency.amountnumber 或 string是 (写了 currency 时)—非负数, 或可被表达式引擎求值的字符串金额. 写数字就直接用; 写字符串会交给 CoreLib 的 ExpressionEngine 求值, 里面可以用 %count%. 既不是数字也不是非空字符串时, 这个 currency 段会被当成没配置
tiers[].itemsection否发货档位部分含此项见下方两行用物品支付的代价
tiers[].item.sourcestring是 (写了 item 时)—CoreLib ItemSource token这个键缺失或者只有空白时, 整个 item 段会被当成没配置
tiers[].item.amountint否1 (没写或写得不是数字时)> 0; ≤ 0 时该 item 段视为未配置需要交多少个这种物品
fallbacksection否见示例见下方三行备用档: 超出所有 tiers 之后, 每一格的价格
fallback.currencysection否见示例键集同 tiers[].currency备用档用货币支付的代价
fallback.itemsection否无键集同 tiers[].item备用档用物品支付的代价. fallback 下两种价格都没写时不生效
fallback.max_amountnumber是 (要用备用档时)—> 0单格价格的上限. 这个键缺失或者不是正数时, 备用档整体失效, 超出 tiers 的格子就买不了
batchsection否enabled: true, options: [1, 5, 10, 64]见下方两行批量购买的选项
batch.enabledboolean否truetrue / false设为 false 时, 一次点击固定只买 1 格
batch.optionslist<int>否[1, 5, 10, 64]只接受正整数元素; 过滤后为空则回退默认值一次点击可以买几格的选项. 目前界面只取列表第一项: 点扩容按钮固定买 options[0] 格, 其余选项在这个模块里没有入口, 留着它们只是预先保留的能力

逐格计价 ​

先弄清计价方式: 批量购买是按每一格分别算价再相加来结算的, 不是"单价乘数量". 玩家买第 N 格时, 由 N 落在哪个 count_range 决定这一格的价格; N 从 purchasedSlots + 1 开始往上走. 如果按当前单价乘数量来算, 玩家就能在最便宜的那一档一次买下一大批槽位, 把本该属于贵档的格子按低价锁住. 发货默认 batch.options: [1, 5, 10, 64] 而界面只取第一项, 所以按发货配置下单只有 1 格, 逐格相加就退化成单格报价; 你把 options[0] 改大就能一次买多格, 这时才会真正体现出逐格累加

表达式里的 %count% 表示"这次要买的第几格". 没有档位命中的时候就用 fallback

比如这样: 1–5 格用金币, 之后每格用钻石, 并放开一次买 10 格

yaml
tiers:
  - count_range: "1-5"
    currency:
      type: vault
      currency: "coin"
      amount: 500

fallback:
  item:
    source: "diamond"
    amount: 2
  max_amount: 100000

batch:
  enabled: true
  options: [10]

max_amount 护栏 ​

注意: fallback.max_amount 是必须写的, 缺失或者不是正数时备用档整体失效, 超出 tiers 的格子就买不了 (回报 no_price_defined). 它的作用是防止指数表达式溢出成 Infinity

作用范围要分清: 这个上限只管备用档, 命中 tiers 的格子不受 max_amount 限制, 它们只受"金额必须有限且非负"这一条约束 (违反时报 price_overflow)

当某一格算出来的价格超过 max_amount 时, 插件会明确拒绝这次报价 (price_over_cap), 而不是不报错地把它压到上限: 你写的公式算出了越过自己设的上限的价格, 这次购买就不该继续

购买入口与前置校验 ​

购买入口只有一个: GUI 里的 unlock 按钮. 你点击之后会依次做这些检查

  1. unlock.purchase_enabled 为 true, 否则回报"未开放"
  2. 玩家要有 emakistorage.unlock.purchase, 否则回报没有权限
  3. unlock_costs.yml 里要有能用的档位或者能用的备用档, 否则回报 no_price_defined
  4. 这次购买不会突破 capacity.max_slots (effectiveSlots + 购买格数 > max_slots 时就回报 max_slots_reached)

一次下单买几格, 取 batch.options 的第一项, 见上面 batch.options 的说明

支付顺序与逆序补偿 ​

一次购买按这个顺序走: 报价 → 触发可取消的 StorageUnlockEvent → 扣货币 → 扣物品 → 增加 purchasedSlots

不用怕多扣: 扣款之后的任何一步失败, 都会按反过来的顺序补偿, 先把已经扣掉的物品按倒序退回去, 再退还货币, 然后才返回失败. purchasedSlots 在这条补偿路径上从来没被改过

失败时按"宁可拒绝也不放过"处理: 既没有匹配到档位, 也没有能用的备用档时拒绝购买, 不会当成免费

相关页面 ​