refactor(combat): 重构战斗系统伤害计算逻辑并完善文档

- 将getNeAttrs函数移动到更合理的位置
- 重构伤害计算逻辑,明确区分施法者和被攻击者属性使用
- 完善接口和核心方法的文档注释
- 修正暴击伤害计算错误,使用施法者暴击伤害属性
- 优化闪避判定公式,考虑施法者命中率
This commit is contained in:
2025-11-21 09:18:49 +08:00
parent 3990799046
commit 8a0cfb78dd
2 changed files with 182 additions and 24 deletions

View File

@@ -17,18 +17,7 @@ export enum NeAttrs {
IN_BURN = 2, IN_BURN = 2,
IN_POISON = 3, IN_POISON = 3,
} }
export const getNeAttrs = () => {
let reAttrs = {};
Object.keys(NeAttrs).forEach(key => {
if (!isNaN(Number(key))) {
reAttrs[Number(key)] = {
value: 0,
time: 0,
};
}
});
return reAttrs;
}
// ========== 属性枚举 ========== // ========== 属性枚举 ==========
/** /**
* 英雄属性枚举 * 英雄属性枚举
@@ -140,6 +129,18 @@ export const getAttrs = () => {
return reAttrs; return reAttrs;
} }
export const getNeAttrs = () => {
let reAttrs = {};
Object.keys(NeAttrs).forEach(key => {
if (!isNaN(Number(key))) {
reAttrs[Number(key)] = {
value: 0,
time: 0,
};
}
});
return reAttrs;
}
/** /**
* 属性类型配置表 * 属性类型配置表
* 用于区分每个属性是数值型还是百分比型 * 用于区分每个属性是数值型还是百分比型

View File

@@ -9,13 +9,30 @@ import { DamageQueueComp, DamageEvent, DamageQueueHelper } from "./DamageQueueCo
import { smc } from "../common/SingletonModuleComp"; import { smc } from "../common/SingletonModuleComp";
/** 最终伤害数据接口 */ /** 最终伤害数据接口
* 用于封装一次攻击计算的所有结果数据
* @property damage - 最终造成的伤害值(已考虑所有加成和减免)
* @property isCrit - 是否为暴击攻击
* @property isDodge - 是否被闪避闪避时damage为0
*/
interface FinalData { interface FinalData {
damage: number; damage: number;
isCrit: boolean; isCrit: boolean;
isDodge: boolean; isDodge: boolean;
} }
/** 业务层业务逻辑处理对象 伤害处理系统 */ /**
* 英雄攻击系统 - 伤害处理核心系统
*
* 职责:
* 1. 处理所有伤害事件的计算和分发
* 2. 管理伤害队列的处理流程
* 3. 协调视图层的表现更新
*
* 重要概念:
* - damageEvent.Attrs: 施法者属性快照(创建技能时保存)
* - targetAttrs: 被攻击者实时属性
* - 属性来源规范:攻击判定用施法者,防御判定用被攻击者
*/
@ecs.register('HeroAtkSystem') @ecs.register('HeroAtkSystem')
export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
@@ -81,10 +98,30 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** /**
* 执行攻击计算 * 执行攻击计算 - 核心伤害计算逻辑
* @param target 目标实体 *
* @param damageEvent 伤害事件数据 * 属性使用规范(重要!):
* @returns 最终伤害数据 *
* ✅ 正确使用施法者属性damageEvent.Attrs - 快照):
* - CRITICAL: 暴击率判定
* - CRITICAL_DMG: 暴击伤害加成
* - BACK_CHANCE: 击退概率
* - HIT: 命中率(用于闪避计算)
* - AP/MAP: 攻击力(基础伤害计算)
*
* ✅ 正确使用被攻击者属性targetAttrs - 实时):
* - DODGE: 闪避率(用于闪避计算)
* - SHIELD_MAX: 护盾最大值(护盾吸收)
* - hp: 当前生命值(伤害应用)
* - 各种抗性属性(预留扩展)
*
* ❌ 错误使用案例(已修复):
* - 不要混用施法者和被攻击者的属性进行同一计算
* - 暴击伤害应该基于施法者的暴击伤害属性
*
* @param target 目标实体(被攻击者)
* @param damageEvent 伤害事件数据(包含施法者信息和属性快照)
* @returns 最终伤害数据(包含伤害值、暴击标记、闪避标记)
*/ */
private doAttack(target: ecs.Entity, damageEvent: DamageEvent): FinalData { private doAttack(target: ecs.Entity, damageEvent: DamageEvent): FinalData {
const targetAttrs = target.get(HeroAttrsComp); const targetAttrs = target.get(HeroAttrsComp);
@@ -107,19 +144,27 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
this.onAttacked(target); this.onAttacked(target);
// 闪避判定 // 闪避判定
const isDodge =this.checkChance(targetAttrs.Attrs[Attrs.DODGE] || 0); // 闪避成功概率 = 被攻击者闪避率 - 施法者命中率
// targetAttrs.Attrs[Attrs.DODGE]: 被攻击者的实时闪避属性
// damageEvent.Attrs[Attrs.HIT]: 施法者在技能创建时的命中属性快照
const isDodge =this.checkChance((targetAttrs.Attrs[Attrs.DODGE]-damageEvent.Attrs[Attrs.HIT]) || 0);
if (isDodge) { if (isDodge) {
// TODO: 触发闪避视图表现 // TODO: 触发闪避视图表现
reDate.isDodge=true; reDate.isDodge=true;
return reDate; return reDate;
} }
// 暴击判定 // 暴击判定
// 使用施法者的暴击率属性damageEvent.Attrs 快照)
const isCrit = this.checkChance(damageEvent.Attrs[Attrs.CRITICAL]); const isCrit = this.checkChance(damageEvent.Attrs[Attrs.CRITICAL]);
if (isCrit) attackerModel?.clearTalBuffByAttr(Attrs.CRITICAL); if (isCrit) attackerModel?.clearTalBuffByAttr(Attrs.CRITICAL);
// 计算伤害 // 计算伤害
let damage = this.dmgCount(damageEvent.Attrs,damageEvent.s_uuid); let damage = this.dmgCount(damageEvent.Attrs,damageEvent.s_uuid);
if (isCrit) { if (isCrit) {
damage = Math.floor(damage * (1 + (FightSet.CRIT_DAMAGE + targetAttrs.Attrs[Attrs.CRITICAL_DMG]) / 100)); // 暴击伤害计算
// 使用施法者的暴击伤害加成属性damageEvent.Attrs 快照)
// 公式:最终伤害 = 基础伤害 * (1 + 系统暴击倍率 + 施法者暴击伤害加成)
const casterCritDmg = damageEvent.Attrs[Attrs.CRITICAL_DMG] || 0;
damage = Math.floor(damage * (1 + (FightSet.CRIT_DAMAGE + casterCritDmg) / 100));
reDate.isCrit=true; reDate.isCrit=true;
} }
// 伤害计算考虑易伤等debuff // 伤害计算考虑易伤等debuff
@@ -130,7 +175,9 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
// 应用伤害到数据层 // 应用伤害到数据层
targetAttrs.hp -= damage; targetAttrs.hp -= damage;
targetAttrs.atked_count++; targetAttrs.atked_count++;
//击退判定 // 击退判定
// 使用施法者的击退概率属性damageEvent.Attrs 快照)
// 击退成功后需要清理施法者的相关天赋buff
const isBack = this.checkChance(damageEvent.Attrs[Attrs.BACK_CHANCE] || 0); const isBack = this.checkChance(damageEvent.Attrs[Attrs.BACK_CHANCE] || 0);
if (isBack) attackerModel?.clearTalBuffByAttr(Attrs.BACK_CHANCE); if (isBack) attackerModel?.clearTalBuffByAttr(Attrs.BACK_CHANCE);
@@ -155,7 +202,19 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
reDate.damage=damage; reDate.damage=damage;
return reDate; return reDate;
} }
//伤害计算,暂时简单计算 /**
* 基础伤害计算
* 根据技能配置和施法者属性计算基础伤害值
*
* @param CAttrs 施法者属性快照(包含攻击力等关键属性)
* @param s_uuid 技能ID用于获取技能配置
* @returns 基础伤害值(未考虑暴击、抗性等因素)
*
* @todo 后续可以扩展更多伤害计算公式:
* - 技能倍率加成
* - 元素伤害计算
* - 真实伤害/魔法伤害/物理伤害区分
*/
private dmgCount(CAttrs:any,s_uuid:number){ private dmgCount(CAttrs:any,s_uuid:number){
let sConf = SkillSet[s_uuid]; let sConf = SkillSet[s_uuid];
if (!sConf) return 0; if (!sConf) return 0;
@@ -164,6 +223,16 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
} }
/** /**
* 处理角色死亡 * 处理角色死亡
*
* 死亡处理流程:
* 1. 标记死亡状态is_dead = true
* 2. 触发死亡事件onDeath
* 3. 记录调试信息(如启用调试模式)
*
* @param entity 死亡的实体
*
* @important 死亡状态一旦设置,该实体将不再处理新的伤害事件
* 这确保了死亡逻辑的单一性和一致性
*/ */
private doDead(entity: ecs.Entity): void { private doDead(entity: ecs.Entity): void {
const model = entity.get(HeroAttrsComp); const model = entity.get(HeroAttrsComp);
@@ -182,7 +251,26 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** /**
* 暴击判定 * 统一概率判定方法
*
* 用于所有概率相关的判定:
* - 暴击率判定
* - 闪避率判定
* - 击退概率判定
* - 其他特殊效果概率
*
* @param rate 概率值0-100的百分比
* @returns true-判定成功false-判定失败
*
* @example
* ```typescript
* // 10%概率触发
* if (this.checkChance(10)) {
* // 触发特殊效果
* }
* ```
*
* @important 概率为0或负数时直接返回false避免不必要的随机数计算
*/ */
private checkChance(rate: number): boolean { private checkChance(rate: number): boolean {
if (rate <= 0) return false; if (rate <= 0) return false;
@@ -192,6 +280,16 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** /**
* 伤害计算考虑易伤等debuff * 伤害计算考虑易伤等debuff
*
* 预留的伤害计算扩展点,用于处理:
* - 被攻击者的易伤状态(增加受到伤害)
* - 被攻击者的伤害减免状态(减少受到伤害)
* - 元素抗性计算
* - 真实伤害/魔法伤害/物理伤害的类型区分
*
* @param model 被攻击者的属性组件(包含抗性、易伤等状态)
* @param baseDamage 基础伤害值
* @returns 最终伤害值(经过各种加成和减免后的结果)
*/ */
private calculateDamage(model: HeroAttrsComp, baseDamage: number): number { private calculateDamage(model: HeroAttrsComp, baseDamage: number): number {
// 这里可以添加易伤等debuff的计算逻辑 // 这里可以添加易伤等debuff的计算逻辑
@@ -201,6 +299,15 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** /**
* 护盾吸收伤害 * 护盾吸收伤害
*
* 护盾吸收逻辑:
* 1. 如果护盾值 >= 伤害值完全吸收剩余伤害为0
* 2. 如果护盾值 < 伤害值:部分吸收,剩余伤害 = 原伤害 - 护盾值
* 3. 护盾被击破时,重置护盾最大值属性
*
* @param model 被攻击者的属性组件(包含当前护盾值)
* @param damage 原始伤害值
* @returns 剩余伤害值(护盾吸收后的结果)
*/ */
private absorbShield(model: HeroAttrsComp, damage: number): number { private absorbShield(model: HeroAttrsComp, damage: number): number {
if (model.shield <= 0) return damage; if (model.shield <= 0) return damage;
@@ -222,6 +329,15 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** /**
* 被攻击时触发的事件 * 被攻击时触发的事件
*
* 预留的扩展点,用于处理被攻击时的特殊逻辑:
* - 触发反伤效果(荆棘光环等)
* - 触发被攻击天赋(如受击回血、受击反击等)
* - 触发特殊状态(如受伤狂暴、受伤护盾等)
*
* @param entity 被攻击的实体
*
* @todo 当前对怪物实体直接返回,后续可以根据需求扩展怪物的被攻击逻辑
*/ */
private onAttacked(entity: ecs.Entity): void { private onAttacked(entity: ecs.Entity): void {
const model = entity.get(HeroAttrsComp); const model = entity.get(HeroAttrsComp);
@@ -235,6 +351,21 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** /**
* 死亡时触发的事件 * 死亡时触发的事件
*
* 根据实体阵营类型处理不同的死亡逻辑:
*
* - FacSet.MON怪物触发掉落逻辑
* - 延迟执行掉落,避免阻塞主逻辑
* - 可以扩展:经验值计算、任务进度等
*
* - FacSet.HERO英雄触发英雄死亡特殊处理
* - 游戏结束判定
* - 复活机制检查
* - 死亡惩罚/奖励
*
* @param entity 死亡的实体
*
* @important 死亡事件应该幂等,避免重复触发
*/ */
private onDeath(entity: ecs.Entity): void { private onDeath(entity: ecs.Entity): void {
const model = entity.get(HeroAttrsComp); const model = entity.get(HeroAttrsComp);
@@ -251,6 +382,19 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** /**
* 延迟执行掉落逻辑 * 延迟执行掉落逻辑
*
* 采用延迟执行的原因:
* 1. 避免在伤害计算过程中阻塞主线程
* 2. 给死亡动画播放留出时间
* 3. 可以批量处理多个掉落,优化性能
*
* @param entity 死亡的怪物实体
*
* @todo 具体实现可以包括:
* - 根据怪物等级计算基础掉落
* - 幸运值影响掉落品质
* - 特殊事件(双倍掉落、稀有掉落等)
* - 掉落物在场景中的生成位置计算
*/ */
private scheduleDrop(entity: ecs.Entity): void { private scheduleDrop(entity: ecs.Entity): void {
// 这里可以添加掉落逻辑 // 这里可以添加掉落逻辑
@@ -259,6 +403,19 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** /**
* 延迟执行英雄死亡逻辑 * 延迟执行英雄死亡逻辑
*
* 英雄死亡的特殊处理,比普通怪物复杂:
*
* 处理内容包括:
* - 检查复活次数和复活条件
* - 触发游戏结束界面(如适用)
* - 记录死亡统计信息
* - 处理死亡惩罚(经验损失、装备损坏等)
*
* @param entity 死亡的英雄实体
*
* @important 英雄死亡通常需要玩家交互,所以必须延迟处理
* 给玩家足够的反馈时间和操作空间
*/ */
private scheduleHeroDeath(entity: ecs.Entity): void { private scheduleHeroDeath(entity: ecs.Entity): void {
// 这里可以添加英雄死亡的特殊处理 // 这里可以添加英雄死亡的特殊处理