Cataclysm-DDA 近战武器平衡电子表格全解:从 0.E 再平衡项目到 DPS 评估公式的底层原理
2026/9/15 13:18:55 网站建设 项目流程

Cataclysm-DDA 近战武器平衡电子表格全解:从 0.E 再平衡项目到 DPS 评估公式的底层原理

【免费下载链接】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 仓库内 MELEE_BALANCE_SPREADSHEET.md 为线索,完整还原推动 0.E 实验版近战武器再平衡项目(Melee Weapon Evaluation 电子表格)的 7 个数据选项卡及其数据流,并结合 GAME_BALANCE.md 与src/melee.cppsrc/item_gun_tool_ammo.cpp源码,讲透命中、暴击、破甲伤害与每秒伤害(DPS)的评估公式。读完你将掌握:如何用"武器类别 + 命中要素 + 伤害模型"给任意近战武器打分、为什么 rapid strike 会"快速弹刀"、以及nodachizweihander这类同价值武器是如何被调平到同一档位的。

一、背景:一份驱动了全游戏近战武器改版的电子表格

Cataclysm-DDA 的 0.E 实验版经历了一次大规模近战武器再平衡(melee weapon rebalance)项目。该项目的决策依据,是维护者 mlangsdorf 构建的一份名为"Melee Weapons Evaluation"的复杂电子表格(原文档自述)。这份电子表格最初以 Google Docs 形式托管,其静态副本被保留在仓库中,即同目录下的 Melee Weapon Evaluation.ods,便于在外部托管变动时随时回溯。

需要特别说明的是:原文档开头明确标注"This document is quite out of date"(本文档已相当过时)。它描述的是 0.E 时代(约 2019 年 10 月基于 0.D 实验版)的评估方法,其中的具体数值与当前版本可能已有出入,但评估框架、数据流与平衡思路至今仍是理解本游戏近战数值体系的钥匙,并与现行 GAME_BALANCE.md 中的武器平衡准则一脉相承。

二、电子表格总览:7 个选项卡与数据流

这份电子表格"机关众多、环环相扣",共有 7 个核心选项卡,数据按以下路径流转:

Raw(原始数据倾倒) │ ▼ Filter(按武器类别过滤)──► New Formula(新公式评估) │ ▼ New Formula Sorted(按类别排序比较) │ ▼ Proposed Values(拟议数值调整) │ ▼ Proposed Values Sorted(调整后再排序) │ ▼ Comparison(汇总对比)

一句话概括:先灌入全量武器数据(Raw),过滤出要分析的类别(Filter),用新公式逐列计算评估值(New Formula),排序找出异常值(New Formula Sorted),然后手工调整数值(Proposed Values),再次排序验证(Proposed Values Sorted),最后汇总新旧对比(Comparison)

三、Raw 选项卡:初始数据与附加分类维度

Raw 是初始数据倾倒,数据取自2019 年 10 月某个 0.D 实验版,且使用维护者 mlangsdorf 惯常加载的模组(因此其中出现了大量 Blazemod 内容)。在原始物品数据之外,Raw 选项卡又手工附加了几个关键分类列,这些列是后续所有评估的基础:

含义说明
Weapon Class武器类别(简单数字评级)0=不是武器;1=爆炸装置;2=远程武器/枪械;3=可作为武器的工具(如煮锅);4=头盔;5=可作为好武器的工具(如大锤);6=临时/伪制武器;7=真正的专用武器
Category武器归类(便于同类比较)近战类别包括:Ax、Ax2、Club、Club2、Flail、Flail2、Knife、Polearm、Spear、Staff、Shortsword、Sword、Sword2。以 2 结尾的类别均为双手武器
Acc重算命中值依据下方命中要素重新计算武器命中。因为人工复查发现大量武器的命中值并不符合 GAME_BALANCE.md 中规定的判定标准
Grip握持见 GAME_BALANCE.md 中 To-Hit Value 一节
Leng长度同上
Surf打击面同上
Bal平衡同上,且在原版 Clumsy 之外额外扩展了更多档位,由人工目测赋分

这里的 Acc/Grip/Leng/Surf/Bal 五列直接对应 GAME_BALANCE.md 中描述的to_hit 值描述性体系。在正式武器 JSON 中,武器不再直接写一个"to_hit": X数字,而是写成一组描述词,由游戏运行时从基础值 -2 自动累加计算:

"to_hit": { "grip": "(parameter)", "length": "(parameter)", "surface": "(parameter)", "balance": "(parameter)" }

各参数档位如下(均取自 GAME_BALANCE.md):

Grip(握持,衡量你能多好地控制武器以应对情况变化)

  • -1 "bad":特别难握持的物品(天生湿滑、圆润无棱边的篮球、木桶,或带锋利边缘危险的碎金属、碎玻璃)
  • +0 "none":不属于以下类别的任何物体(2x4 木方、显示器、电线、毒刺、衣物等"可抓但太粗/太细/太软"的东西)
  • +1 "solid":握持相当扎实的武器,如管道、石块、吉他琴颈、台球杆
  • +2 "weapon":专为手形设计的握把(剑、斧、刀、警棍),或绑在身上/身体一部分的武器(拳头在此得 +2,使其总分归零,因为其他项都不适用)

Length(长度,更大的接触面与更安全的打击距离)

  • +0 "hand":从握持处延伸不足 31 cm(1 英尺)
  • +1 "short":延伸 31 cm ~ 91 cm(1~3 英尺)
  • +2 "long":延伸超过 91 cm(3 英尺)

Striking Surface(打击面,衡量武器对打击方式的要求)

  • -2 "point":单点武器(镐、矛、注射器),必须以特定方式命中才能造成可观伤害;也包括攻击角度刁钻的武器(如镰刀)
  • -1 "line":线状伤害武器(剑、刀),存在攻击角度;有尖端但无需实打实命中也有威力的武器(如钉板)也归此类
  • +0 "any":任意角度攻击(棍棒、管道、狼牙棒),任何表面都是有效打击面
  • +1 "every":即使擦伤也能造成显著伤害的武器(锯齿撕裂类、电击棒)

Balance(平衡,衡量物品是否适合挥舞/突刺)

  • -2 "clumsy":非常笨重或重心失衡,光是稳住都费力(煎锅、电锯、椅子、吸尘器)
  • -1 "uneven":重心不均匀,但不妨碍挥舞(斧头、大锤、步枪、镰刀、多数长柄武器)
  • +0 "neutral":中性平衡(重棍、岩石、台球杆、厨刀、羊角锤、金属管、撬棍、手枪)
  • +1 "good":非常适合挥舞或突刺(棒球棒、高尔夫球杆、剑、四分之一杖、刀)

这套"描述词驱动"体系正是 Raw 选项卡中 Acc 列能重算命中值的原因:只要武器 JSON 里写的是 grip/length/surface/balance 四词组合,电子表格就能按同一套基准重新求值,从而揪出那些"写了个漂亮 to_hit 数字、却不符合任何物理特征"的武器。

四、Filter 选项卡:按武器类别切片

Filter 从 Raw 取得初始数据,并读取单元格 B1 中的 Weapon Class 值,把所有武器类别低于该值的物品过滤掉,从而缩小分析范围。这是整个数据流的第一道闸门——后续所有选项卡都只分析通过筛选的武器,便于集中对比同一"武器段位"内的物品。

五、New Formula 选项卡:新武器评估公式逐列拆解

这是整份电子表格的核心,计算公式相当复杂。其布局与计算逻辑如下:

评估者设定:所有武器都交由一名力量 10、敏捷 10、感知 10,全部武器技能 4 级的幸存者来评估。基础命中(base hit)、属性暴击(stat crit)、技能暴击(skill crit)、钝击倍率(bash mult)、切割倍率(cut mult)、刺击倍率(stab mult)等基准值,均取自 src/melee.cpp 的相关代码段。

前 7 行是表头数据,主体计算从第 8 行开始。各列含义:

名称含义
AAverage该武器的新评估值,放在表格边缘便于引用
B–J(原始属性)武器原始数据,经 Filter 选项卡从 Raw 取来
Kroll_hit评估者 base_hit 与武器 acc 之和
LWpn Crit武器对暴击的贡献
M–N3 Crit / 2 Crit基于技能、命中、属性的三连暴击与双连暴击发生概率
O–QAverage Non-Crit3 类伤害各自在未暴击情况下的平均伤害(破甲前)

接下来三个数据块是本表最复杂的部分:列 S–Z、AB–AI、AK–AR 结构完全重复,但分别采用三种不同怪物属性(对应 GAME_BALANCE.md 中描述的三个参照怪物:低闪避高装甲的 soldier zombie、中等闪避中等装甲的 survivor zombie、高闪避无装甲的 smoker zombie)。每个数据块内:

  • S "Hits":每 1000 次攻击的期望命中次数,按均值 = 5 × ( roll_hit − 怪物 Dodge )、标准差 = 25 的正态分布计算
  • T "Crit %s":这些命中里暴击所占百分比
  • U "Dmg":破甲后的平均期望伤害,即对 3 种伤害类型分别计算max( 0, 伤害 − 护甲 )后求和
  • V "Crit":破甲后的平均期望暴击伤害。3 种伤害类型的暴击倍率不同、暴击时削减的有效护甲量也不同,因此公式远比普通伤害复杂
  • W–Y:重复伤害与暴击伤害计算,但针对rapid strike(快速打击),其基础伤害按 2/3 折算
  • Z "Dmg/Turn":每回合伤害,有两种口径:
    • 无 rapid strike 时:100 × ( Dmg × ( Hits − crits ) + Crit Dmg × crits ) / ( 1000 × Moves )——即每次普通命中伤害 × 普通命中数 + 每次暴击伤害 × 暴击命中数,除以 1000 次攻击消耗的总行动点数,再乘以每秒 100 行动点
    • 有 rapid strike 时:100 × ( Dmg × ( Hits − crits ) / 2 + Crit Dmg × crits / 2 + rapid Dmg × ( Hits − crits ) / 2 + rapid Crit Dmg × crits / 2 ) / ( 1000 − ( hits / 2 ) × Moves + Hits × 0.33 × Moves )——即普通打击与快速打击各占一半次数、伤害与行动消耗均按比例分摊

收尾两列

  • AT "Weapon":重复武器名,便于对照
  • Au "Value":列 Z、AI、AR(三种怪物的 Dmg/Turn)的平均值Reach 2 武器再乘 1.5,Reach 3 武器乘 1.75

值得注意的历史差异:GAME_BALANCE.md 现行版本中 reach 的权重是Reach 2 +20%、Reach 3 +35%,而 0.E 时代这份电子表格采用了更激进的×1.5 / ×1.75(即 +50% / +75%)。这说明 reach 的价值权重在项目演进中被下调过,阅读旧文档时需留意。

5.1 源码印证:正态命中与查表命中

电子表格中"均值 5 × ( roll_hit − Dodge )、标准差 25 的正态分布"并非凭空设计,它与 src/melee.cpp 的实际实现精确对应:

  • Character::get_melee_hit_base()返回Character::get_hit_base() + hit_weapon + mabuff_tohit_bonus() + 附魔修正,其中hit_weapon即武器 acc 贡献(src/melee.cpp)
  • Character::hit_roll()综合敏捷、技能、武器与武术,再叠加远视、倒地、蹲伏、乘车等修正,最终调用melee::melee_hit_range( hit )(src/melee.cpp)
  • melee::melee_hit_range的实现正是normal_roll( accuracy * 5, 25.0f )——均值 accuracy×5、标准差 25 的正态分布掷点,与电子表格的 Hits 列公式完全同源(src/melee.cpp)

而现行代码中item::effective_dps()(src/item_gun_tool_ammo.cpp)沿用了同一套评估思想,但实现为查表而非正态分布逐次计算:

const float mon_dodge = mon.get_dodge(); float base_hit = guy.get_dex() / 4.0f + guy.get_hit_weapon( *this ); base_hit *= std::max( 0.25f, 1.0f - guy.avg_encumb_of_limb_type( bp_type::torso ) / 100.0f ); float mon_defense = mon_dodge + mon.size_melee_penalty() / 5.0f; constexpr double hit_trials = 10000.0; const int rng_mean = std::max( std::min( static_cast<int>( base_hit - mon_defense ), 20 ), -20 ) + 20; double num_all_hits = hits_by_accuracy[ rng_mean ];

其中rng_mean将(基础命中 − 怪物防御)钳制到 [−20, 20] 后作为hits_by_accuracy查表下标,对应电子表格中"每 1000 次攻击的期望命中数";base_hit - 1.5 * mon_dodge则对应电子表格的 "2 Crit" 双暴击通道(详见下节)。effective_dps()还会对 10000 次试击统计总行动点与总伤害,换算回每秒伤害,与电子表格 Z 列的"每回合伤害"口径一致。

5.2 源码印证:暴击的双通道机制

电子表格中 M–N 列区分了"3 Crit(三连暴击)"与"2 Crit(双连暴击)",这对应 GAME_BALANCE.md 描述的双暴击系统:双连暴击(double critical)在第二次命中掷点对抗 1.5 × 怪物闪避时触发,发生概率高于普通暴击;每次攻击会分别计算两种暴击的出现概率。effective_dps()中的实现印证了这一分层:

const int rng_high_mean = std::max( std::min( static_cast<int>( base_hit - 1.5 * mon_dodge ), 20 ), -20 ) + 20; double num_high_hits = hits_by_accuracy[ rng_high_mean ] * num_all_hits / hit_trials; double double_crit_chance = guy.crit_chance( 4, 0, *this ); // 双连暴击通道 double crit_chance = guy.crit_chance( 0, 0, *this ); // 普通暴击通道

即:部分命中具备触发双连暴击的资格(base_hit − 1.5 × dodge对应的命中区间),其余命中走普通暴击通道,两者用不同的 crit_chance 参数分别折算。顺带一提,GAME_BALANCE.md 作者自己也吐槽"这套暴击系统又蠢又复杂、会产生怪异的数值结果",并提议改为"原始命中掷点高于均值 1 个标准差即触发双暴击"的更简方案——这段注释至今仍保留在文档中。

六、New Formula Sorted:揪出同类武器中的异常值

New Formula Sorted 把旧武器评估值新公式评估值并列,并按武器类别(Category)内新评估值降序排序。这样做的目的,是快速发现某类武器中"鹤立鸡群"或"垫底"的异类——原文档点名了broadsword(阔剑)这个在当时明显超模的例子。排序视图让再平衡工作有了明确靶子:先看类别内分布是否均匀,再对极端值动刀。

七、Proposed Values:拟议数值的调整规则

这是整个再平衡项目的"手术台"。Proposed Values 选项卡复刻了 New Formula 的结构,但将 C–J 列复制为静态数值(paste as values),然后手工微调,把评估值"凑成漂亮的数字"。其调整规则有六条,条条都值得细读:

  1. 命中值全面重算:命中(Accuracy)改为完全依据 Raw 选项卡中的 grip、length、striking surface、balance 四要素取值。于是,一批原本命中虚高的武器(如bionic claws 生化爪)被削弱,而一批命中被低估的武器(如katana 武士刀)得到加强。

  2. 同类别武器评估值大致拉平:同一 Category 内的武器被调整到大致相同的评估值,但数值分布仍各有差异。总体倾向是:欧洲武器的伤害更多来自钝击(bash)、更少来自切割(cut),且通常更重更慢;亚洲武器则相反。原文档承认这种设定"有点随意、也不太写实,但大家都这么期待"。最典型的案例是两把双手剑:

    • 日本nodachi(野太刀):攻击速度 164,钝击 6,切割 47
    • 欧洲zweihander(双手剑):攻击速度 169,钝击 18,切割 39
    • 两者评估值都约为26,殊途同归。
  3. 劣质(inferior)武器降档:与真实武器相比,劣质版获得1 档平衡惩罚,同时一半的切割/刺击伤害转为钝击,且切割/刺击伤害降至真实武器的1/4。示例:一把劣质长枪(inferior pike)为钝击 30、刺击 11,而正版长枪为钝击 8、刺击 44

  4. 伪制(fake)武器再降档:伪制武器与劣质版一样糟糕的平衡,但基本是钝器,因此打击面(striking surface)通常升为 "every"——讽刺的是,这让伪制武器反而比真品更准或一样准。其钝击伤害为劣质版的一半,切割/刺击伤害再降到劣质版的 1/4(即真实武器的 1/16)。示例:一把伪制长枪为钝击 15、刺击 3

  5. 大量武器获得伤害提升以拉高评估值:原因是很多低伤害、带 rapid strike 的武器被严重高估——破甲伤害测试表明,rapid strike 常常只是让你"以两倍速度弹刀"(bounce off armor twice as fast),低伤害打不穿护甲,快速连击毫无意义。

  6. 长矛与长柄武器特殊处理

    • Spears(矛):曾有一套基于重量的刺击伤害粗略公式(原作者已无法找回),总体效果是矛与矛之间的差异比以前更小了。
    • Polearms(长柄武器):在距离 2 时获得与 Ax2 相同的粗略伤害,但在距离 1 时单独开一条平衡线,原始伤害乘以 0.7。这使长柄武器在远距离非常强悍,但贴身肉搏时比四分之一杖(quarterstaff)略逊一筹。

八、Proposed Values Sorted 与 Comparison:验证与汇总

  • Proposed Values Sorted:与 New Formula Sorted 同构的对比选项卡,只不过数据源换成 Proposed Values 调整后的数值,用于验证调整后类别内是否足够均匀。
  • Comparison(汇总选项卡)
    • 列 A–P:每个通过 Filter 的武器都列出其当前属性与拟议属性、原始评估值、新公式评估值、使用拟议数值的新公式评估值。对拟议数值而言,提升的值标绿、变差的值标红——一眼就能看出改动方向,方便对比变更、抓出错误。
    • 列 S–AH:重复同样流程,但单元格S1、T1、U1、V1 可手工填入类别名,下方小计仅汇总匹配这些类别的武器子集,从而简化同类比较。

九、历史局限性与今天的平衡基准

原文档的自述"quite out of date"应当被认真对待:

  • 数据源是 2019 年 10 月的 0.D 实验版(含个人模组),覆盖面与当前主分支差异巨大;
  • reach 权重(×1.5/×1.75)、部分伤害模型与现行 GAME_BALANCE.md 已不一致;
  • 现行文档对武器价值的评估已收敛为清晰的武器层级(weapon tiers)<2非武器(塑料瓶、石块、靴子)→2–5工具与临时武器(木板、尖棍、管道、锤子)→6–11危险工具或粗糙专用武器(高尔夫球杆、木矛、匕首矛、手斧、弹簧刀、tonfa、四分之一杖)→12–15优秀专用武器(木斧/消防斧、钢矛、电动切肉刀、库克锐刀、木剑、砍刀、铁丝钉棒)→20–35为杀人而设计的战争武器(胁差、武士刀、阔剑、双手剑、战斗刀、战斧、战锤、狼牙棒、晨星)→35+科幻武器(钻石武士刀、单分子刃、光剑、链锯剑),并给出具体锚点值:战斗刀 20、短刃 22、长刃/短斧/短链枷 24、双手刃/长斧/多数矛 26、双手斧与长柄武器 28、战斗矛 30,临时武器约为真品价值的 75%。这些锚点正是 0.E 电子表格方法论沉淀后的现行版本。

十、结语:一份可复现的"武器评分流水线"

回顾整份电子表格,它本质上是一条可复现的武器数值评审流水线:Raw(全量数据 + 分类标签)→ Filter(切片)→ New Formula(命中/暴击/破甲/DPT 全链路求值)→ 排序找异常 → 按六条规则手工调整 → 汇总验证。其中"命中 = 均值 5×(roll_hit−dodge)、标准差 25 的正态分布"至今仍是 src/melee.cpp 中melee_hit_range的真实实现;"三参照怪物求平均 DPS"的思想则演化为 src/item_gun_tool_ammo.cpp 的item::effective_dps()查表实现。

如果你要为本游戏新增或平衡一件近战武器,标准动作是:先在 data/json 中写好to_hit四要素描述词(确保符合 GAME_BALANCE.md 的握持/长度/打击面/平衡准则),再对照现行武器层级锚点设定伤害与攻速,最后用effective_dps的评估思路做一次心理验算——这就是 0.E 那次大改版留给今天的完整方法论遗产。

【免费下载链接】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),仅供参考

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

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

立即咨询