45 美分的抗生素凭什么在灾变后值 2 美元 —— 拆读 Cataclysm-DDA 的 price_postapoc 定价管线
2026/9/17 4:50:44 网站建设 项目流程

45 美分的抗生素凭什么在灾变后值 2 美元 —— 拆读 Cataclysm-DDA 的 price_postapoc 定价管线

【免费下载链接】Cataclysm-DDACataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.项目地址: https://gitcode.com/GitHub_Trending/ca/Cataclysm-DDA

打开 Cataclysm-DDA 的物品信息面板:一支抗生素并排显示两个价格,灾变前 45 cent,灾变后 2 USD。本文讲清 price_postapoc 默认值、单价怎么算、沿途的动态折价规则,让你能为任意物品独立写出一份合理的灾变后价格配置。

先跑通一个例子:45 美分的抗生素进入交易管线

仓库里抗生素的真实配置只关心其中几行:

// data/json/items/comestibles/med.json { "id": "antibiotics", "name": { "str_sp": "antibiotics" }, "price": "45 cent", "price_postapoc": "2 USD", ... }

在 src/item_info.cpp 里,物品面板同时取price(false)(灾变前)与price(true)(灾变后)两个值展示,所以你看到的是 45 cent 与 200 cent 并排——涨价约 4.4 倍。但如果你背包里这支药已经烂成 rotten,无论配置写多少,交易价都直接是 0,这条路径与你的 JSON 毫无关系。

带着这 4 个问题往下读:

  • 两个价格字段的默认值分别是什么?不写 price_postapoc 时引擎按什么计价?
  • 设置了 stack_size 的物品,JSON 里填的是单价还是整组价格?
  • 文档说的"任何物品不超过 15000 cent",和数据里 250 USD 的货币捆怎么共存?
  • filthy、damage、rot 这些状态各砍掉多少价格?

字段模型:price、price_post 与 stack_size 的默认值

先定几个术语。price_postapoc(灾变后价格,内部字段名 price_post):物品在灾后经济里的交换价值,是本文主角;price(灾变前价格):同一物品灾变前商店里的标价;cent(分,最小货币单位):1 USD = 100 cent;default-sized stack(默认堆叠):JSON 价格所对应的"一满堆"物品。

JSON 字段内部字段默认值含义(单位 cent)出处
priceprice0灾变前价格,针对默认堆叠src/itype.h L1627-L1628
price_postapocprice_post-1(未指定)灾变后价格,针对默认堆叠src/itype.h L1630-L1631
stack_sizestack_size0count_by_charges 物品每堆的件数src/itype.h L1624-L1625
charges—(实例字段)随物品生成当前持有件数/剂量数src/item.cpp L1955-L1957

itype.h 的注释明确写着两个价格都是 "Price given is for a default-sized stack"(价格针对默认大小的堆叠给出)——这是后文一切换算的地基。另外 src/item_factory.cpp L474 附近有一条隐含规则:弹药与食物类物品的 stack_size 若未填写,默认取初始 charges 数,所以"一组抗生素 = 一满堆"这种写法并不需要你手写 stack_size。

规则全景:六档价格区间与 15000 cent 红线

设计依据写在 doc/design-balance-lore/POSTAPOC_PRICE_GUIDE.md。先引原文:

Prices were based on the philosophy that there is a decent portion of humanity unwilling to regularly brave the risks of city scavenging, that are always on the lookout for supplies to stay alive.

定价基于这样一个前提:有相当一部分人不愿经常冒险进城搜刮,他们始终在寻找能活下去的补给。

这个前提直接解释了为什么食物药品被抬高——"稳健买家"愿意用物资高价换生存品。由此收敛出的完整分档:

稀有度实用性单价区间(cent)仓库内参照物
常见原材料~0–25 /kg
常见无实用0旧版美元纸币 price_postapoc 全为 0 cent(currency.json money_one 起)
常见理论用途~10旧硬币 penny 至 dollar coin 均 1 cent
常见实用~250–2000断线钳 250、铝球棒 1250(文档基准例)
稀有无实用~10–50
稀有理论用途~100–500
稀有大用途~1000–15000哥特板甲 12000(文档基准例)

食物另走三因子(卡路里 × 可保存性 × 营养):肉干 250、高能罐头可达 ~500、垃圾食品低至 ~50。货币锚点与分档互相咬合:自由商人本票 FMCNote 定价 2 USD 50 cent(250 cent),恰等于一片肉干;文档还给出生存线——每天带回 2000 cent 勉强温饱、4000 cent 过得不错,可作为你设计商人库存或任务奖励的校准尺。

上限是硬性红线:

No item should be worth more than 15000.They might be worth more if the economy was larger but that's pretty much the most anyone will be willing to spend on any one thing, no matter how nice it is.

任何单件物品都不应超过 15000 cent。如果经济规模更大或许值更多,但这基本就是任何人愿意为单件东西支付的极限,无论它多好。

为什么这样分档:价格由"稀缺度 × 效用"两个轴决定,而 15000 的上限防止天价物品击穿交易平衡——它约束的是"单件",这一点后面会在例外部分用数据验证。

引擎里发生了什么:默认值回退、按堆叠折算与动态折价

规则落到代码里只有四个点,全在加载与计价两条链路上。

① 加载:-1 是"未指定"哨兵,0 是合法价格。src/item_factory.cpp L4484-L4485 读取 JSON:

optional( jo, was_loaded, "price", price, not_negative_money, 0_cent ); optional( jo, was_loaded, "price_postapoc", price_post, not_negative_money, -1_cent );

随后 L462-L465 做回退,对应字段模型表里"默认 -1"那一行:

// use pre-Cataclysm price as default if post-cataclysm price unspecified if( obj.price_post < 0_cent ) { obj.price_post = obj.price; }

② 计价主体:rotten 短路 + 按 charges 折算。src/item.cppprice_no_contents(L1943 起)前半段:

if( rotten() ) { return 0; } int price = price_override ? *price_override : units::to_cent( practical ? type->price_post : type->price ); if( damage() > 0 ) { price -= price * static_cast<double>( std::max( 0, damage_level() - 1 ) ) / 5; }

后半段处理堆叠与状态,正是 itype.h 注释"针对默认堆叠给出"的运行时代价:

if( count_by_charges() || made_of( phase_id::LIQUID ) ) { // price from json data is for default-sized stack price *= charges / static_cast< double >( type->stack_size ); } // Nominal price for perfectly fresh food, decreasing linearly as it gets closer to expiry if( is_food() ) { price *= ( 1.0 - get_relative_rot() ); } if( is_filthy() ) { price = std::max( price - PRICE_FILTHY_MALUS, 0 ); }

回扣规则全景:文档"食品看可保存性"一句在这里落地为线性腐败折价;PRICE_FILTHY_MALUS在 src/item.cpp L150 定义为固定 100 cent;damage 5 级时最多砍掉 80%(源码注释 "maximal reduction is 80% of the value"),即打 2 折。

③ 总价递归:容器内容物逐层累加。同文件 L1921-L1941 的price(bool practical)visit_items遍历所有内容物并累加price_no_contents,枪械另加 gunmods()、工具另加 toolmods():

visit_items( &res, practical { res += e->price_no_contents( practical ); return VisitResponse::NEXT; } );

所以"装满补给的一只背包"在交易界面显示的总价,是自内向外逐层算出来的,不是配置里的某个数。

④ 展示层。面板的两个数即此(src/item_info.cpp L4225-L4226):

const int price_preapoc = price( false ) * batch; const int price_postapoc = price( true ) * batch;

这也是你验证配置的现场:若两行显示相同数值,说明 price_postapoc 没写、走了 ① 的回退。

例外与暗规则:文档、数据与引擎不一致的 4 处

  1. 文档基准例 ≠ 实际数据(抗生素)。文档基准 JSON 写的是"price_postapoc": "400 USD"、charges 15、stack_size 200;而实际数据 data/json/items/comestibles/med.json L87-L88 是"price": "45 cent""price_postapoc": "2 USD"。400 USD ÷ 200 = 200 cent/剂,两处的单价其实一致——文档示例是"整组价"写法。不注意的话,你会把 400 USD 原样抄进另一个 stack_size 不同的物品,单价直接失真数倍。
  2. 成捆货币突破 15000 红线。data/json/items/generic/currency.json 里 money_strap_FMCNote(100 张 merch)标 250 USD = 25000 cent,money_bundle_FMCNote(1000 张)标 2500 USD。15000 约束的是"单件物品",捆扎物是整捆总价——但反过来,你为一个普通单件物品写超 15000 就是违反文档红线。
  3. 回退哨兵是 -1 不是 0。money_one 显式写"price_postapoc": "0 cent":0 是合法价格,不触发回退。真正危险的是高价值物品漏写 price_postapoc——比如灾变前 900 USD 的 Hub 01 金币若漏写,灾变后仍按 90000 cent 交易,直接击穿红线;而漏写低价值物品则几乎无感,这类错误最难被肉眼发现。
  4. IRREPLACEABLE_CONSUMABLE 目前是"无行为标志"。它注册于 data/json/flags.json L2167,大量弹药与药品挂着它,但文档自述 "In the future we can implement a feature that will allow this items to increase in price over time"——当前它不改变任何价格,只是预留钩子。不注意的话,你会以为打了这个标志就能做动态涨价平衡,白做一版。

另有两条状态性暗规则:filthy 是固定减 100 cent(250 cent 的断线钳沾污后卖 150,≤100 cent 的物品沾污直接归零);rotten 无条件短路为 0,发生在所有折算之前,配置写得再贵也救不回来。

实操自检清单:7 步完成一次定价

    1. 在 POSTAPOC_PRICE_GUIDE.md 的分档表中定位"稀有度 × 实用性"区间,选出 ≤15000 cent 的单价
    1. 食品按三因子估算:对标肉干 250、高能罐头 ~500、垃圾食品 ~50
    1. 有 stack_size 的物品,确认你填的是整组价,并心算除法复核单价落档(400 USD ÷ 200 = 200 cent/剂)
    1. 判断是否不可再生的灾变前消耗品(药品、不可重装弹药、咖啡茶叶),是则加IRREPLACEABLE_CONSUMABLE
    1. 与四个锚点比对:抗生素 200 cent/剂、断线钳 250、铝球棒 1250、哥特板甲 12000
    1. 检查漏写后果:该物品灾变前价格是多少?回退后会不会击穿 15000 红线(item_factory.cpp L462-L465)
    1. 模拟边界状态过一遍:rotten → 0;filthy → −100 cent;damage 5 级 → 剩 20%(item.cpp L1943-L1975)

收尾:这套管线的位置与下一步

price_postapoc 是灾后交易系统的核心数据字段:设计文档划定分档与红线,itype.h 定义字段与默认值,item_factory 完成回退,item.cpp 在运行时叠加堆叠折算与状态折价,item_info 负责展示。本文覆盖了从字段默认值到动态折价的全链路,以及文档与数据不一致的 4 处差异。新增物品时,建议先过一遍自检清单第 3、6 步再动笔——单价除法和漏写回退是两处最容易翻车的地方。

延伸阅读:

  • doc/design-balance-lore/POSTAPOC_PRICE_GUIDE.md
  • data/json/items/generic/currency.json
  • data/json/items/comestibles/med.json
  • src/item.cpp
  • src/item_factory.cpp

【免费下载链接】Cataclysm-DDACataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.项目地址: https://gitcode.com/GitHub_Trending/ca/Cataclysm-DDA

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询