攻击和防御类属性(ATTACK_AND_DEFENSE)
概述
ATTACK_AND_DEFENSE 是最常用的属性类型。当一个实体攻击另一个实体时,所有此类型的属性都会被触发。
触发场景:
- 玩家用剑/斧攻击生物
- 玩家射箭命中实体
- 生物攻击玩家
- 生物攻击生物
不触发场景:
- 摔落伤害、溺水、火焰(这些属于
ACCIDENTAL_DAMAGED) - 药水伤害
需要重写的方法
你的属性类需要重写以下方法:
/**
* 属性启用时调用(注册监听器、启动任务等)
*/
@Override
void onEnable()
/**
* 攻击发生时调用(核心逻辑)
* @param attacker 攻击者
* @param entity 被攻击者(防御方)
* @param handler 处理器,提供伤害/属性/消息等操作方法
* @return true=属性已触发; false=未触发
*/
@Override
boolean onAttackAndDefense(
LivingEntity attacker,
LivingEntity entity,
AttackAndDefenseHandler handler
)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 点伤害:
@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
}
}第二级:读取属性值
让伤害量取决于实体的属性面板值:
@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 防御方护甲值:
@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 支持多个参数,会自动拼接成一行。
第四级:概率机制
加入暴击判断——概率触发,触发后额外倍率:
@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→ 必定返回truepercentage <= 0→ 必定返回false- 中间值 → 按百分比概率随机返回
第五级:吸血机制
在造成伤害后按比例治疗攻击者:
@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 事件,保证其他插件能感知到治疗行为。
取消攻击
@Override
boolean onAttackAndDefense(LivingEntity attacker, LivingEntity entity, AttackAndDefenseHandler handler) {
// 某个条件不满足时,直接取消本次攻击
if (某种条件) {
handler.setCancelled(true)
handler.addMessage(attacker, "§c攻击被取消!")
return false
}
return true
}调用 handler.setCancelled(true) 后:
- 本次攻击的伤害归零
- 后续优先级更低的 ATTACK_AND_DEFENSE 属性仍会执行,但它们应该检查
handler.isCancelled()来决定是否跳过 - 原版伤害粒子(红心)也会被取消(需 ProtocolLib)
判断攻击类型
@Override
boolean onAttackAndDefense(LivingEntity attacker, LivingEntity entity, AttackAndDefenseHandler handler) {
// 判断是否为远程攻击(弓箭等)
if (handler.isProj()) {
// 远程专属逻辑
if (handler.isArrowProj()) {
// 箭矢专属
}
} else {
// 近战逻辑
}
// 获取伤害蓄力强度(0~100,近战=攻速,弓箭=拉弓强度)
double intensity = handler.getDamageIntensity()
// ...
}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.yml 中 trigger setting.ATTACK_AND_DEFENSE.cancel default damage 是否为 true。如果为 true,原版伤害被取消,你需要在自己的属性中通过 handler.addDamage() 来提供伤害。
Q: 怎么让属性只对玩家生效?
if (attacker.getType() != EntityType.PLAYER) return falseQ: 多个 ATTACK_AND_DEFENSE 属性如何控制执行顺序? 通过 priority(优先级)参数。数值越小越先执行。例如:
- 命中率判断 → priority=1
- 伤害计算 → priority=2
- 吸血效果 → priority=3
