跳转至

2.2 穿深与伤害值

penetrationbaseDamage 是上下文中两个最核心的数值字段。前者决定"能不能打穿",后者决定"打穿了造成多少伤害"。本节解释它们的含义、与装甲等级体系的关系,以及武器侧在构造上下文时如何在它们之间分配设计意图。

穿深(penetration)

penetration 字段的类型是 float,单位为 mm RHA(轧制均质装甲等效厚度)。它描述的是弹丸在当前命中条件下的有效穿深——也就是说,角度修正应该已经包含在这个值中。

RHA 作为协议双方——武器侧和护甲侧——共同使用的参考标准,扮演了度量衡的角色。无论你的武器在设定中是发射 120mm 口径的 APFSDS 还是 7.62mm 步枪弹,无论目标的装甲是轧制钢板还是复合陶瓷加爆反,双方最终在"等效 RHA 厚度"这个统一的单位下进行比较。武器的设计者根据弹丸的实际物理属性(初速、弹芯材料、长径比等)折算出一个垂直 RHA 穿深;护甲的设计者根据装甲的物理构成和倾斜角度折算出一个等效 RHA 厚度。两者可以直接数值比较。

penetration 为 0 时,表示该伤害没有穿甲能力——它对装甲目标的判定永远为未击穿。Float.MAX_VALUE 则用作"绝对穿透"的标记——任何有限装甲都无法阻挡。在实际使用中,Float.MAX_VALUE 常用于环境伤害或指令伤害等无需穿甲判定的场景——配合 ArmorLevel.SUPER_HEAVY_6 的上限 2000mm RHA,甚至更低的数值即可实现对所有常规等级的击穿。需要注意的是,即使 Float.MAX_VALUE 也无法击穿 ArmorLevel.UNPENETRABLE——该元等级专为绝对防御设计,它在离散等级判定中会使 canDefeat 返回 false,在精密模式下 medianRha() 返回的也是 Float.MAX_VALUE,两者在此数值上相等。

穿深与装甲等级

上下文的辅助方法 getPenetrationLevel()penetration 映射为 ArmorLevel 枚举。这个映射由 ArmorLevel.fromRha(float) 完成——取第一个 upperRha >= penetration 的等级,即向上映射。

因此 120mm 穿深会落在 SUPER_HEAVY_2(80~150mm),35mm 穿深会落在 HEAVY(20~40mm),而 0 穿深落在 UNARMORED_1(0~1mm)。武器侧可以在发起伤害后通过此方法了解到"我的武器在离散等级视角下是什么穿甲等级"——这主要用于 UI 显示(HUD 上的穿甲等级图标)和粗略的对标判断,实际的穿甲判定在精密模式下直接使用 float 比较而非等级。

角度修正:武器侧的责任

协议中 modifyPenetration 方法的默认实现直接返回 ctx.penetration()——它不做任何默认的斜穿修正。斜穿修正是武器侧的职责。这个设计决策有几个理由:

首先,正确的角度修正需要精确的入射角和弹丸的穿深衰减模型。不同的弹药类型有不同的斜穿特性——APFSDS 长杆弹在斜角下的穿深衰减比全口径 AP 弹小得多,HEAT 破甲弹则有完全不同的锥形装药射流行为。将这些复杂物理内置在协议层中不是它的职责范围。协议提供的是比较框架,具体的物理模型由各武器模组自行实现。

其次,将修正放在武器侧意味着护甲侧拿到的是一个"已经扣除了角度效应的有效穿深",modifyPenetration 可以专注于减效(debuff)逻辑——爆反拦截、间隙衰减等——而不用担心自己在叠加别人已经做过的修正。

基于这些考虑,武器侧在调用协议前应按以下方式做角度修正。弹丸速度矢量与命中面法线反方向之间的夹角 θ 决定了等效穿深的放大倍数。标准简化模型为 有效穿深 = 垂直穿深 / cosθ——越倾斜的命中(cosθ 越小),等效需要穿透的装甲越厚。

Vec3 shotDir = bulletVelocity.normalize();
double cosTheta = Math.abs(shotDir.dot(hitNormal));   // < 0.01 时接近平行,视为跳弹

float correctedPen = cosTheta < 0.01f
    ? Float.MAX_VALUE   // 极端斜角,按跳弹或无限厚处处理
    : (float)(rawPenetration / cosTheta);

这个修正应该填入 penetration 字段。护甲侧通过比较 modifyPenetration(ctx)getRHA(ctx) 来判定击穿。

标称伤害(baseDamage)

baseDamage 是弹丸在理想命中条件下的原始伤害期望——即如果完全穿透目标且弹药正常工作,应该造成的伤害量。它与 penetration 是两码事:一把高穿深的狙击枪可能只有中等伤害(准确命中要害),而一枚大口径高爆弹可能穿深不高但伤害极高(冲击波和破片)。

标称伤害和穿深的数值对应关系不是线性的——穿深高的武器不一定伤害高,反之亦然。因此武器设计者应该根据武器的设计意图独立设定这两个值,而不是试图在它们之间建立固定公式。

baseDamage 最终被护甲侧的 calculateFinalDamage(ctx, result) 作为基础值使用。默认的三级伤害模型基于穿甲等级比较来调整这个基础值:

穿甲判定结果 等级关系 最终伤害(默认)
PENETRATED 越级击穿(穿深等级 > 护甲等级) baseDamage × 100%
PENETRATED 同级击穿(穿深等级 = 护甲等级) baseDamage × 65%
BLOCKED 未击穿 0
RICOCHET 跳弹 0

这里"同级击穿"意味着武器的穿深等级刚好等于目标的护甲等级——即 ctx.getPenetrationLevel() == target.getArmorLevel(ctx)。按照等级体系的定义(canDefeat 使用 >=),这种情况算击穿,但弹药在穿透过程中被大量消耗,最终伤害打了六五折。越级击穿时弹药几乎完整穿透,伤害不打折。

需要注意的是,上述模型是默认行为。护甲模组完全可以在 calculateFinalDamage 中覆写,实现非零的未击穿钝伤、跳弹贯穿、超匹配加成等效果。

伤害值的最终走向

尽管武器侧设定了 baseDamage,但最终对目标造成的伤害量并不由武器侧决定。协议的五步管线中,baseDamage 经过护甲侧的 calculateFinalDamage 转换后才得到最终伤害,再通过 hurt 方法执行。武器侧的代码只能通过 BFDamageApi.hurt() 的返回值得知实际造成的伤害量:

float dealt = BFDamageApi.hurt(target, ctx);
if (dealt > 0) {
    // 造成了伤害
} else {
    // 未造成伤害(被挡住了)
}

返回值 dealt 是协议管线执行完毕后,如果 hurt 返回了 true,则等于 calculateFinalDamage 的结果;如果 hurt 返回了 false(目标免疫或伤害被原版机制拦截),则为 0。武器侧可以用这个返回值做击杀判定或 UI 反馈(例如显示命中标记颜色)。

设计参考:如何设定穿深与伤害

对于武器模组的设计者,设定弹药的穿深和伤害时可以考虑以下思路。首先确定弹药在现实或设定中的参考对象——例如它大约相当于什么级别的武器——然后在 ArmorLevel 的 13 级体系中锁定一个目标区间。如果弹药的设计意图是击穿 MEDIUM 级(普通车辆),穿深应设在 20mm RHA 左右;如果面对 SUPER_HEAVY_3(冷战早期 MBT),穿深应接近 300mm。

伤害值则取决于弹药击中目标后的毁伤效果。高爆弹穿深不高但伤害大,适合对付轻装甲;穿甲弹穿深高伤害适中,适合对付重装甲。在默认的三级模型下,同级击穿的 65% 折扣意味着刚好能打穿的弹药无法发挥全部伤害——这鼓励玩家升级弹药以获得越级击穿的完整伤害。