跳到主要内容

PRD-031 战斗与伤害结算

文档编号:PRD-031
版本:v1.0
状态:已通过
适用端:服务端 / 客户端
关联模块:ScenesServer、SkillStatusManager、tbl/Combat.xml

1. 背景与目标​

当前伤害计算分散在技能、怪物、脚本中,不同入口的暴击、减伤、元素系数不一致,容易出现“同属性打不同怪伤害差一倍”的问题。本 PRD 统一战斗结算入口,明确结算顺序与优先级,保证 PvE/PvP 结果可解释、可复现、可日志追溯。

目标

  • 所有伤害/治疗/护盾走同一结算管线。
  • 结算顺序固定,任何新增效果只能插入既定阶段。
  • 客户端只做表现,不参与命中、暴击、伤害计算。

不做

  • 不改变现有技能表现和动作资源。
  • 不在本期引入真实弹道物理;远程技能仍按目标判定。

2. 核心规则​

2.1 结算阶段​

一次攻击按以下顺序结算,前一步失败则终止:

  1. 合法性校验:施法者存活、目标合法、距离/视野、CD、消耗。
  2. 命中判定:命中率 - 目标闪避率,结果夹在 5%–95%。
  3. 暴击判定:暴击率 - 目标抗暴率,上限 80%。
  4. 基础伤害:攻击力 × 技能系数 + 固定伤害。
  5. 防御减伤:按护甲/魔抗分段函数计算。
  6. 百分比减伤:Buff、装备、称号等同类相加,上限 80%。
  7. 元素克制:火/冰/雷/毒克制系数,PvE 与 PvP 分开配置。
  8. 等级压制:等级差超过 5 级时按配置修正。
  9. 护盾吸收:先扣护盾,再扣 HP。
  10. 最终伤害:不低于 max(1, 攻击力 × 1%)。
  11. 反伤/吸血/击杀触发:在伤害落地后结算,反伤不再触发反伤。

2.2 PvP 修正​

  • PvP 伤害统一乘以 pvp_coefficient(默认 0.6)。
  • 治疗在 PvP 中乘以 pvp_heal_coefficient(默认 0.8)。
  • 控制类效果在 PvP 中持续时间乘以 pvp_control_coefficient(默认 0.7),并受韧性缩短。

2.3 死亡​

  • HP ≤ 0 立即进入死亡状态,清除可清除 Buff,停止 AI/技能。
  • 客户端播放死亡表现,服务端广播 SC_EntityDeath。
  • 玩家死亡后可选择复活点复活、道具复活、他人复活;怪物死亡进入掉落流程。

3. 服务端设计​

3.1 模块职责​

模块职责
CombatManager统一伤害/治疗入口,按阶段结算
AttributeManager提供结算用的属性快照,避免结算中属性变化
SkillStatusManager提供 Buff 修正、护盾、免疫、反伤
ScenesServer地图内实体管理、死亡广播、掉落触发
RecordServer战斗日志、死亡记录、异常伤害告警

3.2 核心数据结构​

struct DamageContext {
uint64_t attacker_id;
uint64_t target_id;
uint32_t skill_id;
int64_t base_damage;
int32_t damage_type; // 物理/魔法/真实
int32_t element; // 无/火/冰/雷/毒
bool is_pvp;
bool can_crit;
bool can_block;
uint32_t request_id; // 幂等/日志追踪
};

struct DamageResult {
int64_t final_damage;
int64_t shield_absorb;
int64_t hp_after;
bool is_hit;
bool is_crit;
bool is_block;
bool is_kill;
uint32_t flags;
};

3.3 协议​

方向协议说明
C→SCS_Attack普攻,带目标 ID、客户端时间
C→SCS_SkillCast技能释放,带技能 ID、目标 ID/坐标
S→CSC_DamageResult单次伤害结果,含暴击/格挡/护盾标记
S→CSC_HealResult治疗结果
S→CSC_EntityDeath实体死亡,含击杀者
S→CSC_CombatState进入/脱离战斗状态

协议号统一在 base/SessionCommand.h 分配;新增字段追加在末尾,旧客户端忽略未知字段。

3.4 结算流程​

CS_Attack/CS_SkillCast
→ 校验角色状态、距离、CD、消耗
→ 冻结攻击/目标属性快照
→ 命中 → 暴击 → 基础伤害 → 防御 → 百分比减伤
→ 元素 → 等级压制 → 护盾 → 最终伤害
→ 扣 HP、触发反伤/吸血/击杀
→ 广播 SC_DamageResult / SC_EntityDeath
→ 写战斗日志(异步)

3.5 并发与幂等​

  • 同一 request_id 只结算一次,重复请求返回缓存结果。
  • 同一目标同一帧内多个伤害按到达顺序串行结算,避免 HP 扣成负数。
  • 反伤/吸血使用“已落地伤害”计算,不允许递归触发。
  • 战斗日志异步写入,不阻塞主循环;日志丢失不影响结算。

4. 客户端设计​

4.1 交互流程​

  1. 玩家点击目标或按 Tab 选中目标。
  2. 点击普攻/技能,客户端做动作预表现并发送请求。
  3. 服务端返回 SC_DamageResult 后,客户端以服务端结果为准刷新血条、飘字、死亡。
  4. 客户端本地预判只用于动作衔接,不预扣血、不预判死亡。

4.2 UI 与表现​

  • 目标血条:显示当前/最大 HP、护盾条、Buff 图标。
  • 伤害飘字:物理白字、魔法蓝字、暴击黄字加大、治疗绿字、护盾吸收灰字。
  • 受击:目标闪白/后仰,暴击额外震屏(可在设置中关闭)。
  • 死亡:目标倒地、灰化,玩家弹出复活面板。
  • 战斗状态:进入战斗后禁止上坐骑/回城,脱战 5 秒后恢复。

4.3 协议处理​

  • SC_DamageResult 按 attacker_id + target_id + request_id 去重,防止重复飘字。
  • 收到伤害时若目标已死亡,忽略后续非击杀类表现。
  • 断线重连后拉取场景实体全量状态,不依赖丢失的伤害包恢复血条。

5. 配置与数值​

tbl/Combat.xml 示例字段:

字段类型说明
idint配置 ID
damage_typeint1 物理 / 2 魔法 / 3 真实
pvp_coefficientfloatPvP 伤害系数
crit_capfloat暴击率上限
hit_minfloat最低命中率
defense_kfloat防御减伤曲线系数
level_gap_kfloat等级压制系数
min_damage_ratefloat最低伤害比例

公式:

防御减伤率 = defense / (defense + K × attacker_level)
最终伤害 = 基础伤害 × (1 - 防御减伤率) × (1 - 百分比减伤) × 元素系数 × 等级系数

6. 边界与异常​

场景处理
攻击者与目标不在同一地图拒绝,返回 ERR_TARGET_NOT_IN_MAP
目标已死亡拒绝,客户端停止动作
距离超过技能范围 + 容忍值拒绝并记录异常日志
同一 request_id 重复到达返回首次结果,不重复扣血
伤害导致 HP 为负HP 夹到 0,溢出伤害不传递
反伤导致攻击者死亡正常死亡,不再触发攻击者反伤
结算中目标被传送以快照目标为准,若已离开地图则取消后续触发

7. 埋点与运营​

  • combat_damage:attacker、target、skill、damage、crit、element、pvp。
  • combat_death:死亡实体、击杀者、地图、时间。
  • combat_abnormal:伤害超过阈值、命中率异常、重复 request_id。
  • 后台可按地图/职业/等级段查看平均伤害与死亡率。

8. 验收标准​

  • 同属性同技能对同一目标连续 100 次,伤害分布符合配置概率。
  • 暴击、格挡、闪避、护盾、反伤、吸血按顺序生效,无重复触发。
  • 客户端飘字与服务端日志伤害值一致。
  • 重复发送同一 request_id 不会造成二次伤害。
  • 结算过程中目标死亡/传送/掉线不产生异常或刷物品。
  • 单场 200 人同屏战斗,结算平均耗时低于 5ms/次。