Skip to content

伤害系统 ​

伤害类型文件要写清的内容如下: 一次伤害是怎么一步步算出来的, 每个阶段配哪些参数, 原版伤害怎么被接管

伤害类型文件放在 damage_types/*.yml. 伤害不是用一条固定公式算的, 而是走 stages 多阶段管线 (一步一步叠加的计算流程). 每个伤害类型都能配自己的攻击, 防御, 暴击, 穿透和恢复规则

伤害类型配置 ​

顶层字段 ​

字段类型必填默认值说明
idstring是—伤害类型唯一 ID, 例如 physical, projectile, spell
display_namestring否同 id显示名称
aliaseslist<string>否[]别名列表, 用于兼容旧配置或简写
hard_lockboolean否false要不要强接管这个类型的伤害. 为 true 时, 匹配到的原版伤害事件会被完全替换为属性伤害计算
allowed_eventslist<string>否[]允许触发此伤害类型的事件名白名单. 空列表表示不限制
descriptionstring否""描述文本
messagestring否—攻击者和目标共用的战斗消息, 可被下面两个字段覆盖
attacker_messagestring否—攻击者收到的消息
target_messagestring否—目标收到的消息
stageslist否[]伤害计算阶段列表
recoveryobject否—战后恢复或吸血计算配置

内置伤害类型 ​

默认生成三个伤害类型文件:

  • physical.yml — 物理伤害 (近战)
  • projectile.yml — 投射物伤害 (弓箭, 三叉戟)
  • spell.yml — 法术伤害

stages 多阶段管线 ​

伤害计算按 stages 列表的顺序一步步执行. 每个阶段读取指定属性, 算完后叠加到最终伤害值上

stage 字段 ​

字段类型必填默认值说明
idstring否"stage"阶段 ID, 用于调试输出
kindenum否FLAT_PERCENT计算类型
sourceenum否ATTACKER属性来源
modeenum否ADD结果叠加方式
roleenum否按 id 推断阶段扮演的角色: NORMAL / CRITICAL / DEFENSE / BLOCK. 省略时按阶段 id 推断: crit, critical → CRITICAL; defense, target_defense → DEFENSE; block, shield_block → BLOCK; 其余 → NORMAL. 自定义 id 如果要参与暴击, 防御或格挡, 请自己显式声明
flat_attributeslist<string>否[]参与平坦加成的属性 ID 列表
percent_attributeslist<string>否[]参与百分比加成的属性 ID 列表
chance_attributeslist<string>否[]参与概率触发的属性 ID 列表
multiplier_attributeslist<string>否[]参与乘数的属性 ID 列表
expressionstring否""自定义计算表达式 (kind=CUSTOM 时使用)
variablesmap否{}本阶段的自定义变量 (kind=CUSTOM 时可以被 expression 引用)
min_resultdouble否—阶段结果下限
max_resultdouble否—阶段结果上限
min_chancedouble否—概率下限
max_chancedouble否—概率上限
min_multiplierdouble否—乘数下限
max_multiplierdouble否—乘数上限

kind 计算类型 ​

值说明
FLAT_PERCENT标准模式: 先算 flat 属性总和, 再乘 percent 属性加成, 最后判断 chance 触发和 multiplier 乘数
CUSTOM自定义表达式模式: 用 expression 字段里的公式计算

CUSTOM 表达式变量 ​

选了 kind: CUSTOM 后, 你在 expression 里可以用这些变量:

变量说明
%input% / %base%进入本阶段时的伤害值
%flat%flat_attributes 汇总值
%percent%percent_attributes 汇总值
%chance%chance_attributes 汇总概率
%multiplier%multiplier_attributes 汇总倍率
%crit%本阶段是否判定暴击, 暴击为 1, 否则为 0
%roll%本次结算使用的随机骰值

内置 physical.yml 的用法示例:

yaml
  - id: crit
    kind: CUSTOM
    source: ATTACKER
    chance_attributes: [physical_crit_rate]
    multiplier_attributes: [physical_crit_damage]
    expression: "%input% * (1 + (%crit% * (%multiplier% / 100)))"

  - id: defense
    kind: CUSTOM
    source: TARGET
    flat_attributes: [physical_defense]
    expression: "max(0, %input% - %flat%)"

表达式为空或算不出来时会回退到默认结果: 没有概率属性时保持 %input% 不变, 否则按暴击倍率折算

阶段自定义变量 ​

除了内置变量, 每个阶段还能用 variables 声明自己的变量, 给本阶段的 expression 用. 变量值可以是数值, 表达式或随机数值配置:

yaml
  - id: overflow_soften
    kind: CUSTOM
    source: ATTACKER
    variables:
      soft_cap: 120
      overflow: "max(0, %input% - %soft_cap%)"
      jitter:
        min: 0.95
        max: 1.05
    expression: "(%input% - %overflow% * 0.5) * %jitter%"

规则:

  • 变量可以引用内置变量 (如 %input%, %multiplier%), 伤害上下文变量, 以及在它之前声明的其他变量
  • 变量按声明顺序解析, 因此 overflow 能引用先声明的 soft_cap. 自引用会被拒绝
  • 同名变量会覆盖内置变量与上下文变量
  • 变量只对声明它的阶段有效, 不会传给后面的阶段
  • 随机数值配置在同一次表达式求值内只掷一次 (多处引用取值一致), 但每个阶段, 每次命中都会重新掷
  • 变量解析失败 (例如值是非数值文本, 或引用了没声明的变量) 会导致整条表达式求值失败, 该阶段结果为 0. 这是表达式引擎一贯的行为, 改过 variables 后建议用 /ea trace 复核

source 属性来源 ​

值说明
ATTACKER从攻击方读取属性
TARGET从防御方读取属性
CONTEXT从上下文读取 (用于特殊场景)

mode 叠加方式 ​

值说明
ADD把阶段结果加到当前伤害值上
SUBTRACT把阶段结果从当前伤害值里减去

内置物理伤害管线 ​

内置 damage_types/physical.yml 的实际配置:

yaml
id: physical
display_name: "物理伤害"
aliases: [physical_damage, phys]
allowed_events:
  - ENTITY_ATTACK
  - ENTITY_SWEEP_ATTACK
hard_lock: true
stages:
  - id: attack
    kind: FLAT_PERCENT
    source: ATTACKER
    mode: ADD
    flat_attributes: [physical_attack]
    percent_attributes: [physical_damage_bonus]

  - id: crit
    kind: CUSTOM
    source: ATTACKER
    chance_attributes: [physical_crit_rate]
    multiplier_attributes: [physical_crit_damage]
    expression: "%input% * (1 + (%crit% * (%multiplier% / 100)))"

  - id: defense
    kind: CUSTOM
    source: TARGET
    flat_attributes: [physical_defense]
    expression: "max(0, %input% - %flat%)"

  - id: block
    kind: CUSTOM
    source: TARGET
    chance_attributes: [block_rate]
    multiplier_attributes: [block_reduction]
    expression: "%input% * (1 - (%target_blocking% * %crit% * (%multiplier% / 100)))"
    min_result: 0

recovery:
  source: ATTACKER
  resistance_source: TARGET
  flat_attributes: [percentage_lifesteal]
  percent_attributes: [lifesteal]
  resistance_attributes: [lifesteal_resistance]

attacker_message: "<gray>[<gold>%damage_type%</gold>]</gray> <green>你对 <white>%target%</white> 造成了 <yellow>%final_damage%</yellow> 点伤害</green>%critical_suffix%"

target_message: "<gray>[<gold>%damage_type%</gold>]</gray> <red>%attacker%</red> 对你造成了 <yellow>%final_damage%</yellow> 点伤害%critical_suffix%"

阶段按列表顺序执行: 前一阶段的输出就是下一阶段的输入. 想少刷屏的话, 把 attacker_message / target_message 填成空字符串, 对应消息就不会发

recovery 恢复配置 ​

恢复配置用于计算攻击之后的吸血或回复效果

字段类型默认值说明
sourceenumATTACKER回复属性从哪来
resistance_sourceenumTARGET抗性属性从哪来
flat_attributeslist<string>[]平坦回复属性
percent_attributeslist<string>[]百分比回复属性 (按最终伤害的百分比)
resistance_attributeslist<string>[]抗性属性 (会减少回复效果)
expressionstring""自定义回复表达式
min_resultdouble—回复结果下限
max_resultdouble—回复结果上限

原版伤害接管模式 ​

全局 vanilla_event_damage.enabled 是当前原版伤害接管模式的开关:

  • enabled: true: 当前推荐的完美接管模式. Attribute 不取消原版伤害事件, 而是在事件返回前同步结算 EA 伤害并原地改写最终伤害值. 这样能保留原版副作用, 例如着火僵尸点燃玩家, 铁傀儡击飞, 盾牌格挡, 吸收之心, 荆棘, 死亡统计等. 盾牌格挡的归属可以由 shield.mode 改为交给 EA 结算, 见下文"盾牌格挡"
  • enabled: false: 兼容模式. 按旧逻辑取消事件后由 Attribute 重放伤害与反馈, 适合需要保留旧行为, 或要和特殊战斗插件联调的服务器

完美接管模式只改变"伤害如何施加", 不改变当前的伤害数值管线. 结算结果为闪避, 事件取消, 或最终伤害小于等于 0 时, 本次原版事件会被取消. 吸血, 战斗消息和攻击冷却等 Attribute 自身的副作用仍由 Attribute 补发

盾牌格挡 ​

原版盾牌走 EntityDamageEvent.DamageModifier.BLOCKING, 它独立于 EA 的伤害管线. 全局 shield.mode 决定格挡由谁结算

yaml
shield:
  mode: vanilla
  require_facing: true
  facing_angle_degrees: 180
字段默认值说明
modevanillavanilla 保留原版 BLOCKING; attribute 把它清零, 改由伤害阶段结算
require_facingtrue只在 attribute 模式生效, 要不要要求来源位于正面
facing_angle_degrees180正面判定的张角. 180 等价于原版的半平面判定

两种模式的区别 ​

mode: vanilla (默认, 保持既有行为): 举盾时原版 BLOCKING 把 EA 算出的伤害整体抹掉, 因此 EA 的攻击, 暴击, 穿透在举盾时全部不生效. 这个模式下上下文变量 target_blocking 恒为 0, 伤害类型里的 block 阶段等价于不改变伤害

mode: attribute: EA 清零原版 BLOCKING, 改由 block 阶段读取目标的 block_rate 与 block_reduction 结算, 可以做成百分比减伤. 默认基线是 block_rate: 100 / block_reduction: 100, 等价于原版全免, 所以只切换模式不会改变手感; 把 block_reduction 调低即得到百分比格挡

相关属性 ​

这两个都是普通 EA 属性, 所以盾牌数值可以来自 Lore 词条或 PDC, Gem, Strengthen, Forge 和套装也能照常给它们加成

属性 ID含义
block_rate举盾时格挡的触发概率. 为 0 时格挡永不触发
block_reduction格挡触发后削减的伤害百分比. 100 等价于原版全免

数值基线写在 config.yml 的 default_profile.attributes, 物品数值在基线之上叠加, 总值受属性自身 max_value 截断

block 阶段 ​

内置 physical.yml 与 projectile.yml (原版盾牌同样能挡投射物) 都带有该阶段; spell.yml 没有, 因为原版盾牌不挡法术

yaml
  - id: block
    kind: CUSTOM
    source: TARGET
    chance_attributes: [block_rate]
    multiplier_attributes: [block_reduction]
    expression: "%input% * (1 - (%target_blocking% * %crit% * (%multiplier% / 100)))"
    min_result: 0

%target_blocking% 是本次受击是否构成有效举盾 (含正面判定), 取 1 或 0. %crit% 在这里表示 block_rate 概率是否命中

注意事项 ​

  • 自定义 block 阶段时 chance_attributes 必须保留 block_rate 且基线非零, 否则 %crit% 恒为 0, 举盾将完全不减伤
  • %target_blocking% 在所有伤害阶段表达式中始终可用; 不带目标实体的伤害请求会回落到 0
  • 环境伤害没有来源方向, require_facing: true 时不构成正面格挡
  • 格挡与暴击共用同一次随机骰值, 这是 stages 管线一向的行为

合成命中反馈 ​

合成命中反馈主要给兼容模式用. 完美接管模式下, 原版自己就会产生击退, 受伤动画和音效 (具体表现随伤害来源和实体而定). 需要时, synthetic_hit_feedback 会补发击退和受伤音效, 让战斗反馈更接近原版体验

yaml
synthetic_hit_feedback:
  knockback: true
  knockback_strength: 0.4
  hurt_sound: true
字段说明
knockback要不要补发击退
knockback_strength击退强度. 数值越高, 目标位移越明显
hurt_sound要不要播放受伤音效

调参建议:

  • 如果服务器有自定义击退插件, 先关闭 knockback 测试兼容性
  • 如果战斗节奏偏快, 过强的击退会影响近战连段, 可以降低 knockback_strength
  • 如果战斗消息, 粒子和音效较多, 可以关闭 hurt_sound 降低反馈噪音

hard_lock 与 allowed_events ​

hard_lock ​

hard_lock 写在伤害类型上, 意思是"这个类型只接受 allowed_events 里的事件". 它会被解析并随伤害类型定义传递

与 hard_lock_damage 不是同一个字段

config.yml 顶层的 hard_lock_damage 是兼容保留项, 只被配置解析器读取, 运行时没有任何地方用到它, 改它不会改变任何行为 (config.yml 自带的注释就是这个意思). 没被 allowed_damage_causes 映射命中的原版伤害由 vanilla_event_damage 决定: enabled: true 时按 vanilla_event_damage.damage_type 进入属性管线, enabled: false 时只有 allowed_damage_causes 中允许的原因进入 EA, 其余保留原版行为

allowed_events ​

声明哪些原版伤害事件可以触发这个伤害类型. 空列表表示不限制. 常见事件:

  • ENTITY_ATTACK — 近战攻击
  • PROJECTILE — 投射物
  • ENTITY_SWEEP_ATTACK — 横扫攻击