Skip to content

攻击和防御类属性(ATTACK_AND_DEFENSE)

概述

ATTACK_AND_DEFENSE 是最常用的属性类型。当一个实体攻击另一个实体时,所有此类型的属性都会被触发。

触发场景:

  • 玩家用剑/斧攻击生物
  • 玩家射箭命中实体
  • 生物攻击玩家
  • 生物攻击生物

不触发场景:

  • 摔落伤害、溺水、火焰(这些属于 ACCIDENTAL_DAMAGED
  • 药水伤害

需要重写的方法

你的属性类需要重写以下方法:

Groovy
/**
 * 属性启用时调用(注册监听器、启动任务等)
 */
@Override
void onEnable()

/**
 * 攻击发生时调用(核心逻辑)
 * @param attacker 攻击者
 * @param entity   被攻击者(防御方)
 * @param handler  处理器,提供伤害/属性/消息等操作方法
 * @return true=属性已触发; false=未触发
 */
@Override
boolean onAttackAndDefense(
    LivingEntity attacker,
    LivingEntity entity,
    AttackAndDefenseHandler handler
)
JavaScript
function onEnable(attr)

function onAttackAndDefense(attr, attacker, entity, handler)

关于返回值:

  • 返回 true:标记该属性「已触发」。主要用于追踪和统计(可通过 handler.trigger() 查看)
  • 返回 false:标记「未触发」。通常是条件不满足时返回

返回值的真假不会阻止其他属性继续执行。如果你想阻止后续属性,应该使用 handler.setCancelled(true)

处理器能力

ATTACK_AND_DEFENSE 的处理器实现了以下接口,意味着你可以使用这些方法:

接口提供的能力常用方法
DamageTracker伤害管理addDamage, getDamage, setDamage, multiplyDamage
AttributeTracker属性读取getRandomValue, getAttrData, trigger
MessageTracker消息管理addMessage
MetadataTracker临时数据setMeta, getMeta, hasMeta
ProjectileTracker抛射物判断isProj(), isArrowProj(), getProjRandomValue()
Cancellable可取消setCancelled(true), isCancelled()

实战:从简单到复杂

第一级:固定伤害

最简单的攻击属性——直接增加 10 点伤害:

Groovy
@CompileStatic
class FixedDamage extends GroovyAttribute {

    FixedDamage() {
        super(AttributeTypes.ATTACK_AND_DEFENSE, "fixed_damage", "固定伤害", 1, 1.0D, -1.0D)
    }

    @Override
    boolean onAttackAndDefense(LivingEntity attacker, LivingEntity entity, AttackAndDefenseHandler handler) {
        // 直接增加 10 点伤害
        handler.addDamage(attacker, 10.0D)
        attacker.sendMessage("§e固定伤害 +10!")
        return true
    }
}

第二级:读取属性值

让伤害量取决于实体的属性面板值:

Groovy
@CompileStatic
class ScalableDamage extends GroovyAttribute {

    ScalableDamage() {
        super(AttributeTypes.ATTACK_AND_DEFENSE, "scalable_damage", "成长伤害", 1, 1.0D, -1.0D)
    }

    @Override
    boolean onAttackAndDefense(LivingEntity attacker, LivingEntity entity, AttackAndDefenseHandler handler) {
        // 从攻击者面板读取「成长伤害」的随机值(范围值则随机取)
        double damage = handler.getRandomValue(attacker, "成长伤害")

        if (damage > 0) {
            handler.addDamage(attacker, damage)
            handler.addMessage(attacker, "§f成长伤害触发!造成 ${String.format("%.2f", damage)} 点伤害")
            return true
        }
        return false
    }
}

物品 Lore:成长伤害: 50-100,则每次攻击造成 50~100 点随机伤害。

第三级:攻防对抗

加入防御方的属性——攻击方攻击力 vs 防御方护甲值:

Groovy
@CompileStatic
class AttackDefense extends GroovyAttribute {

    AttackDefense() {
        super(AttributeTypes.ATTACK_AND_DEFENSE, "attack_defense", "攻防计算", 1, 1.0D, -1.0D)
        // 注册 DEFAULT 子属性
        Utils.registerDefaultAttribute("defense", "防御力", 0.0D, -1.0D)
    }

    @Override
    boolean onAttackAndDefense(LivingEntity attacker, LivingEntity entity, AttackAndDefenseHandler handler) {
        double attack = handler.getRandomValue(attacker, "攻防计算")   // 攻击方
        double defense = handler.getRandomValue(entity, "防御力")      // 防御方

        double damage = attack - defense
        if (damage > 0) {
            handler.addDamage(attacker, damage)
            handler.addMessage(attacker,
                "§f攻击力 ${String.format("%.0f", attack)} ,敌方防御 ${String.format("%.0f", defense)} ,",
                "§f造成 ${String.format("%.2f", damage)} 点伤害"
            )
            return true
        } else {
            handler.addMessage(attacker, "§7攻击被完全防御!")
            return false
        }
    }
}

handler.addMessage 支持多个参数,会自动拼接成一行。

第四级:概率机制

加入暴击判断——概率触发,触发后额外倍率:

Groovy
@CompileStatic
class CritAttack extends GroovyAttribute {

    CritAttack() {
        super(AttributeTypes.ATTACK_AND_DEFENSE, "crit_attack", "暴击攻击", 1, 1.0D, -1.0D)
        Utils.registerDefaultAttribute("crit_chance", "暴击率", 0.0D, -1.0D)
        Utils.registerDefaultAttribute("crit_damage", "暴击伤害", 0.0D, -1.0D)
    }

    @Override
    boolean onAttackAndDefense(LivingEntity attacker, LivingEntity entity, AttackAndDefenseHandler handler) {
        double attack = handler.getRandomValue(attacker, "暴击攻击")
        double critChance = handler.getRandomValue(attacker, "暴击率")
        double critDamage = handler.getRandomValue(attacker, "暴击伤害")

        double damage = attack

        // 判断是否暴击
        if (Utils.chance(critChance)) {
            handler.trigger(attacker, "暴击率") // 标记暴击触发

            // 计算暴击附加伤害
            double critExtra = attack * critDamage / 100
            damage += critExtra

            handler.addMessage(attacker, "§6§l暴击! §f额外伤害 +${String.format("%.2f", critExtra)}")
        }

        if (damage > 0) {
            handler.addDamage(attacker, damage)
            return true
        }
        return false
    }
}

Utils.chance(percentage) 是插件提供的工具方法:

  • percentage >= 100 → 必定返回 true
  • percentage <= 0 → 必定返回 false
  • 中间值 → 按百分比概率随机返回

第五级:吸血机制

在造成伤害后按比例治疗攻击者:

Groovy
@CompileStatic
class LifestealAttack extends GroovyAttribute {

    LifestealAttack() {
        super(AttributeTypes.ATTACK_AND_DEFENSE, "lifesteal", "吸血攻击", 1, 1.0D, -1.0D)
        Utils.registerDefaultAttribute("lifesteal_rate", "吸血率", 0.0D, -1.0D)
    }

    @Override
    boolean onAttackAndDefense(LivingEntity attacker, LivingEntity entity, AttackAndDefenseHandler handler) {
        double attack = handler.getRandomValue(attacker, "吸血攻击")
        double stealRate = handler.getRandomValue(attacker, "吸血率")

        double damage = attack
        if (damage <= 0) return false

        handler.addDamage(attacker, damage)

        // 吸血逻辑
        if (stealRate > 0) {
            double heal = damage * stealRate / 100
            // safeHeal 会正确触发 EntityRegainHealthEvent
            BukkitUtils.safeHeal(attacker, heal)
            handler.addMessage(attacker, "§c§l吸血 §f+${String.format("%.2f", heal)} 生命")
        }

        return true
    }
}

注意: 治疗要使用 BukkitUtils.safeHeal() 而不是 entity.setHealth()。前者会正确触发 Bukkit 事件,保证其他插件能感知到治疗行为。

取消攻击

Groovy
@Override
boolean onAttackAndDefense(LivingEntity attacker, LivingEntity entity, AttackAndDefenseHandler handler) {
    // 某个条件不满足时,直接取消本次攻击
    if (某种条件) {
        handler.setCancelled(true)
        handler.addMessage(attacker, "§c攻击被取消!")
        return false
    }
    return true
}

调用 handler.setCancelled(true) 后:

  1. 本次攻击的伤害归零
  2. 后续优先级更低的 ATTACK_AND_DEFENSE 属性仍会执行,但它们应该检查 handler.isCancelled() 来决定是否跳过
  3. 原版伤害粒子(红心)也会被取消(需 ProtocolLib)

判断攻击类型

Groovy
@Override
boolean onAttackAndDefense(LivingEntity attacker, LivingEntity entity, AttackAndDefenseHandler handler) {
    // 判断是否为远程攻击(弓箭等)
    if (handler.isProj()) {
        // 远程专属逻辑
        if (handler.isArrowProj()) {
            // 箭矢专属
        }
    } else {
        // 近战逻辑
    }

    // 获取伤害蓄力强度(0~100,近战=攻速,弓箭=拉弓强度)
    double intensity = handler.getDamageIntensity()
    // ...
}

JavaScript 完整示例

JavaScript
var type = "ATTACK_AND_DEFENSE"
var id = "my_full_attack"
var name = "完整攻击"
var priority = 1
var power = 1.0
var max = -1.0

function init(attr) {
    AttributeAPI.registerDefaultAttribute("defense", "防御力", 0.0, -1.0)
    AttributeAPI.registerDefaultAttribute("crit_rate", "暴击率", 0.0, -1.0)
}

function onEnable(attr) {
    // 属性加载时执行(可选)
}

function onAttackAndDefense(attr, attacker, entity, handler) {
    var attack = handler.getRandomValue(attacker, "完整攻击")
    var defense = handler.getRandomValue(entity, "防御力")
    var critRate = handler.getRandomValue(attacker, "暴击率")

    var damage = attack - defense

    // 暴击判断
    if (Math.random() * 100 < critRate) {
        damage *= 2
        handler.trigger(attacker, "暴击率")
        attacker.sendMessage("暴击!")
    }

    if (damage > 0) {
        handler.addDamage(attacker, damage)
        attacker.sendMessage("造成 " + damage.toFixed(2) + " 点伤害")
        entity.sendMessage("受到 " + attacker.getName() + " 的 " + damage.toFixed(2) + " 点伤害")
        return true
    }
    return false
}

常见问题

Q: 为什么攻击没伤害了? 检查 attribute.ymltrigger setting.ATTACK_AND_DEFENSE.cancel default damage 是否为 true。如果为 true,原版伤害被取消,你需要在自己的属性中通过 handler.addDamage() 来提供伤害。

Q: 怎么让属性只对玩家生效?

Groovy
if (attacker.getType() != EntityType.PLAYER) return false

Q: 多个 ATTACK_AND_DEFENSE 属性如何控制执行顺序? 通过 priority(优先级)参数。数值越小越先执行。例如:

  • 命中率判断 → priority=1
  • 伤害计算 → priority=2
  • 吸血效果 → priority=3