PRD-031 战斗与伤害结算
文档编号:PRD-031
版本:v1.0
状态:已通过
适用端:服务端 / 客户端
关联模块:ScenesServer、SkillStatusManager、tbl/Combat.xml
1. 背景与目标
当前伤害计算分散在技能、怪物、脚本中,不同入口的暴击、减伤、元素系数不一致,容易出现“同属性打不同怪伤害差一倍”的问题。本 PRD 统一战斗结算入口,明确结算顺序与优先级,保证 PvE/PvP 结果可解释、可复现、可日志追溯。
目标
- 所有伤害/治疗/护盾走同一结算管线。
- 结算顺序固定,任何新增效果只能插入既定阶段。
- 客户端只做表现,不参与命中、暴击、伤害计算。
不做
- 不改变现有技能表现和动作资源。
- 不在本期引入真实弹道物理;远程技能仍按目标判定。
2. 核心规则
2.1 结算阶段
一次攻击按以下顺序结算,前一步失败则终止:
- 合法性校验:施法者存活、目标合法、距离/视野、CD、消耗。
- 命中判定:
命中率 - 目标闪避率,结果夹在 5%–95%。 - 暴击判定:
暴击率 - 目标抗暴率,上限 80%。 - 基础伤害:
攻击力 × 技能系数 + 固定伤害。 - 防御减伤:按护甲/魔抗分段函数计算。
- 百分比减伤:Buff、装备、称号等同类相加,上限 80%。
- 元素克制:火/冰/雷/毒克制系数,PvE 与 PvP 分开配置。
- 等级压制:等级差超过 5 级时按配置修正。
- 护盾吸收:先扣护盾,再扣 HP。
- 最终伤害:不低于
max(1, 攻击力 × 1%)。 - 反伤/吸血/击杀触发:在伤害落地后结算,反伤不再触发反伤。
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→S | CS_Attack | 普攻,带目标 ID、客户端时间 |
| C→S | CS_SkillCast | 技能释放,带技能 ID、目标 ID/坐标 |
| S→C | SC_DamageResult | 单次伤害结果,含暴击/格挡/护盾标记 |
| S→C | SC_HealResult | 治疗结果 |
| S→C | SC_EntityDeath | 实体死亡,含击杀者 |
| S→C | SC_CombatState | 进入/脱离战斗状态 |
协议号统一在 base/SessionCommand.h 分配;新增字段追加在末尾,旧客户端忽略未知字段。
3.4 结算流程
CS_Attack/CS_SkillCast
→ 校验角色状态、距离、CD、消耗
→ 冻结攻击/目标属性快照
→ 命中 → 暴击 → 基础伤害 → 防御 → 百分比减伤
→ 元素 → 等级压制 → 护盾 → 最终伤害
→ 扣 HP、触发反伤/吸血/击杀
→ 广播 SC_DamageResult / SC_EntityDeath
→ 写战斗日志(异步)
3.5 并发与幂等
- 同一
request_id只结算一次,重复请求返回缓存结果。 - 同一目标同一帧内多个伤害按到达顺序串行结算,避免 HP 扣成负数。
- 反伤/吸血使用“已落地伤害”计算,不允许递归触发。
- 战斗日志异步写入,不阻塞主循环;日志丢失不影响结算。
4. 客户端设计
4.1 交互流程
- 玩家点击目标或按 Tab 选中目标。
- 点击普攻/技能,客户端做动作预表现并发送请求。
- 服务端返回
SC_DamageResult后,客户端以服务端结果为准刷新血条、飘字、死亡。 - 客户端本地预判只用于动作衔接,不预扣血、不预判死亡。
4.2 UI 与表现
- 目标血条:显示当前/最大 HP、护盾条、Buff 图标。
- 伤害飘字:物理白字、魔法蓝字、暴击黄字加大、治疗绿字、护盾吸收灰字。
- 受击:目标闪白/后仰,暴击额外震屏(可在设置中关闭)。
- 死亡:目标倒地、灰化,玩家弹出复活面板。
- 战斗状态:进入战斗后禁止上坐骑/回城,脱战 5 秒后恢复。
4.3 协议处理
SC_DamageResult按attacker_id + target_id + request_id去重,防止重复飘字。- 收到伤害时若目标已死亡,忽略后续非击杀类表现。
- 断线重连后拉取场景实体全量状态,不依赖丢失的伤害包恢复血条。
5. 配置与数值
tbl/Combat.xml 示例字段:
| 字段 | 类型 | 说明 |
|---|---|---|
id | int | 配置 ID |
damage_type | int | 1 物理 / 2 魔法 / 3 真实 |
pvp_coefficient | float | PvP 伤害系数 |
crit_cap | float | 暴击率上限 |
hit_min | float | 最低命中率 |
defense_k | float | 防御减伤曲线系数 |
level_gap_k | float | 等级压制系数 |
min_damage_rate | float | 最低伤害比例 |
公式:
防御减伤率 = 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/次。