跳转至

1.4 护甲模组:响应协议伤害

协议为护甲模组提供了两种接入路径。路径A是让实体实现 BFHurtTarget 接口——适合自定义生物、载具等"实体自身就是装甲目标"的场景。路径B是让护甲物品实现 BFArmorMaterial 接口——适合防弹胸甲、头盔、护腿等"物品给穿戴者提供防护"的场景。

两条路径在协议管线中享有对等的判定能力:简易模式下只需声明等级即可,精密模式下都可以逐层覆写穿深修正、跳弹判定和自定义伤害计算。差异在于操作粒度——BFHurtTarget 以"命中部位"为单位(一次命中走完整管线),BFArmorMaterial 以"装备槽位"为单位(按命中点映射到具体槽位后走管线)。


路径A:实体实现 BFHurtTarget

让自定义实体声明"我会自行处理穿甲判定和伤害后果"。简易模式只需覆写两个方法——getArmorLevelhurt

public class ArmoredZombie extends Zombie implements BFHurtTarget {

    @Override
    public ArmorLevel getArmorLevel(BFDamageContext ctx) {
        // 根据命中部位返回不同等级,此处简化为固定值
        return ArmorLevel.HEAVY;  // 40~80mm,相当于步战车正面装甲
    }

    @Override
    public boolean hurt(DamageSource source, float amount) {
        // 经协议计算后的最终伤害量,委托给原版管线
        return super.hurt(source, amount);
    }

    @Override
    @Nullable
    public BFDamageContext createContextFromVanilla(DamageSource source, float amount) {
        // 返回 null 表示原版伤害不走协议管线
        return null;
    }
}

这就是护甲侧最简实现。当武器侧通过 BFDamageApi.hurt() 发起协议伤害时,协议调用 getArmorLevel 获取离散护甲等级,自动完成穿甲判定和伤害计算——基于等级比较(等于算击穿)、默认三级伤害模型(刚好击穿 65%、越级击穿 100%、未击穿 0)。

深入的 BFHurtTarget 内容——精密模式覆写、跳弹判定、钝伤计算、原版伤害兼容、以及不委托 super.hurt() 的自定义处理——将在第三章逐一展开。下面进入本页的核心:路径B——护甲物品


路径B:护甲物品实现 BFArmorMaterial

如果你的模组是一个防弹衣模组——玩家穿上胸甲就获得弹道防护——你无法修改 Player 类让它实现 BFHurtTargetBFArmorMaterial 正是为此而设计:让你的护甲物品类实现此接口,协议层便自动识别穿戴了该物品的实体,将其纳入穿甲判定管线。零事件订阅、零手动拦截。

最简示例

让一件胸甲物品获得弹道防护,只需覆写一个方法:

public class HeavyChestplate extends ChestplateItem implements BFArmorMaterial {

    public HeavyChestplate(Properties properties) {
        super(ArmorMaterials.IRON, Type.CHESTPLATE, properties);
    }

    @Override
    public ArmorLevel getArmorLevel(EquipmentSlot slot, @Nullable BFDamageContext ctx) {
        if (slot == EquipmentSlot.CHEST) {
            return ArmorLevel.HEAVY;  // 躯干:20~40mm 防护
        }
        return ArmorLevel.UNARMORED_1;  // 其他槽位不由胸甲保护
    }
}

这就是全部。玩家穿上这件胸甲后,任何协议伤害命中玩家时,协议层会自动识别、映射槽位、做穿甲判定并执行伤害。

多件护甲共存

玩家通常会同时穿戴头盔、胸甲、护腿和靴子。协议适配器在处理一次命中时,按以下顺序确定"哪个槽位的护甲参与判定":

  1. 精确匹配:遍历四个槽位,调用每件护甲物品的 mapHitToSlot(wearer, ctx)。返回的槽位与当前槽位一致时,该护甲物品被选中。默认实现按命中点高度占比划分——头部(>85%身高)、躯干(55%~85%)、腿部(35%~55%)、脚部(<35%)。
  2. 兜底策略:如果命中点无效(如原版爆炸伤害的零值命中点),所有 mapHitToSlot 返回 null,适配器取四个槽位 RHA 的加权平均作为整体防护值(胸甲 35%、头盔 30%、护腿 25%、靴子 10%)。未穿戴协议护甲的槽位贡献 RHA=0——爆炸冲击波会找到全身最薄弱的地方。

控制原版伤害接管

默认情况下,穿戴了协议护甲的实体会拦截所有原版伤害(僵尸攻击、TNT 爆炸等)并走穿甲管线——穿深估算为原版伤害量的一半。如果你需要让某些伤害类型绕过护甲(例如魔法伤害不应被钢板阻挡),覆写 createContextFromVanilla 返回 null

@Override
@Nullable
public BFDamageContext createContextFromVanilla(DamageSource source, float amount) {
    // 魔法、虚空、溺水——这些不应该被钢板阻挡,放行原版流程
    if (source.is(DamageTypes.MAGIC)
        || source.is(DamageTypes.OUT_OF_WORLD)) {
        return null;
    }
    // 物理伤害走穿甲判定
    return BFDamageContext.builder()
        .source(source)
        .baseDamage(amount)
        .penetration(amount / 2f)
        .build();
}

适配器遍历所有槽位:所有护甲物品的 createContextFromVanilla 都返回 null 时,原版伤害才退回原版流程;任一护甲物品返回非 null 则整次伤害被接管。

何时需要精密模式

BFHurtTarget 完全对称,BFArmorMaterial 也提供简易/精密双模式:

方法 默认行为 覆写场景
getArmorLevel(slot, ctx) 返回 UNARMORED_1 简易模式入口——声明槽位等级
getRHA(slot, ctx) getArmorLevel 的中位值 需要精确 mm 级等效厚度
modifyPenetration(slot, ctx) 直接返回 ctx.penetration() 爆反拦截(ERA)、间隙衰减
resolvePenetration(slot, ctx) 仅区分 PENETRATED/BLOCKED 跳弹判定(入射角过大 → RICOCHET)
calculateFinalDamage(slot, ctx, result) 三级模型:65%/100%/0 钝伤、超匹配加成

第三章的 3.6 节将详细展开每个方法的设计空间,包括完整精密模式示例(ERA 爆反胸甲)和双层串联管线的运行原理。

护甲不替代原版护甲

BFArmorMaterial 处理的是"穿甲判定"——弹头能否穿透这层护甲。协议层计算出的最终伤害值,在 hurt 执行时仍会经过原版护甲属性(ARMOR / ARMOR_TOUGHNESS)和保护附魔的二次减免。这是一项刻意保留的设计——协议层判"穿不穿",原版数值判"多疼"。


两种路径对比

场景 推荐路径 理由
自定义生物、载具 BFHurtTarget 实体类可控,一次实现覆盖全生命周期的防护
给玩家/原版实体添加防弹衣 BFArmorMaterial 无法修改 Player 类,物品接口是唯一接入点
实体 + 护甲同时存在 两者叠加 协议自动按双层串联管线处理——护甲先拦截,本体后判定

下一步

你已经掌握了护甲侧的两种接入方式。如果希望深入了解装甲等级体系和管线覆写策略,可以跳转到第三章——其中 3.1\~3.5 覆盖 BFHurtTarget 的精密模式,3.6 覆盖 BFArmorMaterial 的完整开发指南。下一节将用一张全景图汇总协议的全部 API 类及其职责。