- 修复在DamageEvent接口和DamageQueueComp缺少ext_dmg和dmg_ratio字段问题 - 修复HeroAtkSystem的伤害计算逻辑 - 优化HeroViewComp的hp_show和mp_show方法,直接使用model数据 - 默认显示血条并增加调试日志输出 - 移除冗余的debug日志,优化伤害计算流程
562 lines
23 KiB
TypeScript
562 lines
23 KiB
TypeScript
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||
import { FacSet } from "../common/config/GameSet";
|
||
import { Attrs } from "../common/config/HeroAttrs";
|
||
import { FightSet } from "../common/config/GameSet";
|
||
import { SkillSet, DType } from "../common/config/SkillSet";
|
||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||
import { HeroViewComp } from "./HeroViewComp";
|
||
import { DamageQueueComp, DamageEvent } from "./DamageQueueComp";
|
||
import { smc } from "../common/SingletonModuleComp";
|
||
|
||
|
||
/** 最终伤害数据接口
|
||
* 用于封装一次攻击计算的所有结果数据
|
||
* @property damage - 最终造成的伤害值(已考虑所有加成和减免)
|
||
* @property isCrit - 是否为暴击攻击
|
||
* @property isDodge - 是否被闪避(闪避时damage为0)
|
||
*/
|
||
interface FinalData {
|
||
damage: number;
|
||
isCrit: boolean;
|
||
isDodge: boolean;
|
||
}
|
||
/**
|
||
* 英雄攻击系统 - 伤害处理核心系统
|
||
*
|
||
* 职责:
|
||
* 1. 处理所有伤害事件的计算和分发
|
||
* 2. 管理伤害队列的处理流程
|
||
* 3. 协调视图层的表现更新
|
||
*
|
||
* 重要概念:
|
||
* - damageEvent.Attrs: 施法者属性快照(创建技能时保存)
|
||
* - TAttrsComp: 被攻击者实时属性
|
||
* - 属性来源规范:攻击判定用施法者,防御判定用被攻击者
|
||
*/
|
||
@ecs.register('HeroAtkSystem')
|
||
export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||
|
||
private debugMode: boolean = false; // 是否启用调试模式
|
||
|
||
/**
|
||
* 过滤器:处理拥有伤害队列的实体
|
||
*/
|
||
filter(): ecs.IMatcher {
|
||
return ecs.allOf(HeroAttrsComp, DamageQueueComp);
|
||
}
|
||
|
||
/**
|
||
* 系统更新(每帧调用)
|
||
* 处理伤害队列中的所有伤害事件
|
||
*/
|
||
update(e: ecs.Entity): void {
|
||
if(!smc.mission.play || smc.mission.pause) return;
|
||
const TAttrsComp = e.get(HeroAttrsComp);
|
||
const damageQueue = e.get(DamageQueueComp);
|
||
|
||
if (!TAttrsComp || !damageQueue || damageQueue.isEmpty()) return;
|
||
|
||
// 标记正在处理
|
||
damageQueue.isProcessing = true;
|
||
|
||
// 处理队列中的所有伤害事件
|
||
let processedCount = 0;
|
||
while (!damageQueue.isEmpty()) {
|
||
const damageEvent = damageQueue.getNextDamageEvent();
|
||
if (!damageEvent) break;
|
||
|
||
// 处理单个伤害事件
|
||
this.doAttack(e, damageEvent);
|
||
processedCount++;
|
||
damageQueue.processedCount++;
|
||
// 如果目标已死亡,停止处理后续伤害
|
||
if (TAttrsComp.is_dead) {
|
||
if (this.debugMode) {
|
||
console.log(`[HeroAtkSystem] ${TAttrsComp.hero_name} 已死亡,停止处理剩余伤害`);
|
||
}
|
||
damageQueue.clear(); // 清空剩余伤害
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 如果队列已空,移除伤害队列组件
|
||
if (damageQueue.isEmpty()) {
|
||
e.remove(DamageQueueComp);
|
||
|
||
if (this.debugMode && processedCount > 0) {
|
||
console.log(`[HeroAtkSystem] ${TAttrsComp.hero_name} 伤害队列处理完成,共处理 ${processedCount} 个伤害事件`);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 执行攻击计算 - 核心伤害计算逻辑
|
||
*
|
||
* 属性使用规范(重要!):
|
||
*
|
||
* ✅ 正确使用施法者属性(damageEvent.Attrs - 快照):
|
||
* - CRITICAL: 暴击率判定
|
||
* - CRITICAL_DMG: 暴击伤害加成
|
||
* - BACK_CHANCE: 击退概率
|
||
* - HIT: 命中率(用于闪避计算)
|
||
* - AP/MAP: 攻击力(基础伤害计算)
|
||
*
|
||
* ✅ 正确使用被攻击者属性(TAttrsComp - 实时):
|
||
* - DODGE: 闪避率(用于闪避计算)
|
||
* - SHIELD_MAX: 护盾最大值(护盾吸收)
|
||
* - hp: 当前生命值(伤害应用)
|
||
* - 各种抗性属性(预留扩展)
|
||
*
|
||
* ❌ 错误使用案例(已修复):
|
||
* - 不要混用施法者和被攻击者的属性进行同一计算
|
||
* - 暴击伤害应该基于施法者的暴击伤害属性
|
||
*
|
||
* @param target 目标实体(被攻击者)
|
||
* @param damageEvent 伤害事件数据(包含施法者信息和属性快照)
|
||
* @returns 最终伤害数据(包含伤害值、暴击标记、闪避标记)
|
||
*/
|
||
private doAttack(target: ecs.Entity, damageEvent: DamageEvent): FinalData {
|
||
const TAttrsComp = target.get(HeroAttrsComp);
|
||
const targetView = target.get(HeroViewComp);
|
||
let reDate:FinalData={
|
||
damage:0,
|
||
isCrit:false,
|
||
isDodge:false,
|
||
}
|
||
if (!TAttrsComp || TAttrsComp.is_dead) return reDate;
|
||
|
||
const caster = damageEvent.caster;
|
||
const CAttrsComp = caster?.ent?.get(HeroAttrsComp);
|
||
|
||
// 获取技能配置
|
||
const skillConf = SkillSet[damageEvent.s_uuid];
|
||
if (!skillConf) return reDate;
|
||
|
||
// 触发被攻击事件
|
||
this.onAttacked(target);
|
||
|
||
// 闪避判定
|
||
// 闪避成功概率 = 被攻击者闪避率 - 施法者命中率
|
||
// TAttrsComp.Attrs[Attrs.DODGE]: 被攻击者的实时闪避属性
|
||
// damageEvent.Attrs[Attrs.HIT]: 施法者在技能创建时的命中属性快照
|
||
const isDodge =this.checkChance((TAttrsComp.Attrs[Attrs.DODGE]-damageEvent.Attrs[Attrs.HIT]) || 0);
|
||
if (isDodge) {
|
||
// TODO: 触发闪避视图表现
|
||
reDate.isDodge=true;
|
||
return reDate;
|
||
}
|
||
// 暴击判定
|
||
// 使用施法者的暴击率属性(damageEvent.Attrs 快照),- 被攻击者的暴击抗性属性TAttrsComp.Attrs[Attrs.CRITICAL_RESIST]
|
||
const isCrit = this.checkChance(damageEvent.Attrs[Attrs.CRITICAL]-TAttrsComp.Attrs[Attrs.CRITICAL_RESIST]);
|
||
// 计算基础伤害
|
||
let damage = this.dmgCount(damageEvent,TAttrsComp);
|
||
console.log("[HeroAtkSystem] dmgCount",damage)
|
||
if (isCrit) {
|
||
// 暴击伤害计算
|
||
// 使用施法者的暴击伤害加成属性(damageEvent.Attrs 快照)
|
||
// 公式:最终伤害 = 基础伤害 * (1 + 系统暴击倍率 + 施法者暴击伤害加成)
|
||
const casterCritDmg = damageEvent.Attrs[Attrs.CRITICAL_DMG] || 0;
|
||
damage = Math.floor(damage * (1 + (FightSet.CRIT_DAMAGE + casterCritDmg) / 100));
|
||
reDate.isCrit=true;
|
||
CAttrsComp?.useValueTalByAttr(Attrs.CRITICAL); // 清除施法者的暴击buff
|
||
|
||
}
|
||
console.log("[HeroAtkSystem] after crit",damage)
|
||
// 护盾吸收
|
||
damage =Math.floor(this.absorbShield(TAttrsComp, damage))
|
||
console.log("[HeroAtkSystem] after shield",damage)
|
||
if (damage <= 0) return reDate;
|
||
// 应用伤害到数据层
|
||
TAttrsComp.hp -= damage;
|
||
if (this.debugMode) {
|
||
const casterName = damageEvent.caster?.ent?.get(HeroAttrsComp)?.hero_name || "未知";
|
||
const casterUuid = damageEvent.caster?.ent?.get(HeroAttrsComp)?.hero_uuid || 0;
|
||
console.log(`[HeroAtkSystem] 英雄${TAttrsComp.hero_name} (uuid: ${TAttrsComp.hero_uuid}) 受到 ${casterName}(uuid: ${casterUuid})的 伤害 ${damage},${isCrit?"暴击":"普通"}攻击,技能ID ${damageEvent.s_uuid}`);
|
||
}
|
||
//反伤判定 并应用到施法者
|
||
this.check_thorns(TAttrsComp, caster?.ent,damage);
|
||
// 击退判定
|
||
// 使用施法者的击退概率属性(damageEvent.Attrs 快照)
|
||
// 击退成功后需要清理施法者的相关天赋buff
|
||
const isBack = this.checkChance(damageEvent.Attrs[Attrs.BACK_CHANCE] || 0);
|
||
if (isBack) CAttrsComp?.useValueTalByAttr(Attrs.BACK_CHANCE);
|
||
|
||
|
||
// ✅ 触发视图层表现(伤害数字、受击动画、后退)
|
||
if (targetView) targetView.do_atked(damage, isCrit, damageEvent.s_uuid, isBack);
|
||
|
||
|
||
// 检查死亡
|
||
if (TAttrsComp.hp <= 0) {
|
||
// 增加被击杀计数
|
||
if (caster) CAttrsComp.Attrs.killed_count++;
|
||
this.doDead(target);
|
||
// ✅ 触发死亡视图表现
|
||
if (targetView) {
|
||
targetView.do_dead();
|
||
}
|
||
}
|
||
|
||
if (this.debugMode) console.log(`[HeroAtkSystem] ${TAttrsComp.hero_name} 受到 ${damage} 点伤害 (暴击: ${isCrit})`);
|
||
|
||
|
||
reDate.damage=damage;
|
||
return reDate;
|
||
}
|
||
check_thorns(TAttrsComp:HeroAttrsComp, caster: ecs.Entity, damage:number) {
|
||
// 检查目标是否有反伤属性,这里受伤的时时施法者
|
||
if (!caster||damage<=0) return;
|
||
let thornsDamage=0;
|
||
thornsDamage=TAttrsComp.Attrs[Attrs.THORNS]||0+TAttrsComp.useCountValTal(Attrs.THORNS);
|
||
let CAttrs=caster.get(HeroAttrsComp);
|
||
// 计算反伤伤害
|
||
let thornsDmg=Math.floor(thornsDamage*damage/100);
|
||
// 应用反伤伤害到数据层
|
||
CAttrs.hp -= thornsDmg;
|
||
CAttrs.atked_count++;
|
||
let CView=caster.get(HeroViewComp);
|
||
// ✅ 触发视图层表现(伤害数字、受击动画、后退)
|
||
if (CView) CView.do_atked(thornsDmg, false, SkillSet[5000].uuid, false);
|
||
// 检查死亡
|
||
if (CAttrs.hp <= 0) {
|
||
this.doDead(caster);
|
||
// 增加击杀计数
|
||
if (caster) TAttrsComp.Attrs.killed_count++;
|
||
// ✅ 触发死亡视图表现
|
||
if (CView) {
|
||
CView.do_dead();
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 详细伤害计算核心方法
|
||
*
|
||
* 这是整个战斗系统中最核心的伤害计算方法,负责根据施法者属性、目标属性和技能配置
|
||
* 计算最终的基础伤害值。该方法采用多阶段的伤害计算公式,综合考虑了物理和魔法伤害
|
||
* 以及各种属性加成和抗性减免。
|
||
*
|
||
* 计算流程:
|
||
* 1. 获取技能配置
|
||
* 2. 计算原始物理伤害和魔法伤害
|
||
* 3. 应用防御减免
|
||
* 4. 应用物理/魔法攻击力和抗性修正
|
||
* 5. 应用元素属性加成和抗性修正
|
||
* 6. 应用最终伤害减免
|
||
* 7. 确保伤害值非负
|
||
*
|
||
* @param CAttrs 施法者属性快照对象,包含所有攻击力、属性加成等战斗属性
|
||
* @param TAttrs 目标属性对象,包含所有防御力、抗性等防御属性
|
||
* @param s_uuid 技能ID,用于获取技能配置信息和伤害类型
|
||
* @returns 经过完整计算后的最终伤害值(未考虑暴击)
|
||
*
|
||
* @important 注意事项:
|
||
* - 此方法计算的是基础伤害,暴击计算在外部处理
|
||
* - 所有除法和乘法计算后都进行取整操作,确保游戏中的伤害值为整数
|
||
* - 元素伤害只应用于魔法伤害部分
|
||
*/
|
||
private dmgCount(damageEvent:DamageEvent,TAttrsComp:HeroAttrsComp){
|
||
// 1. 获取技能配置 - 如果技能不存在,直接返回0伤害
|
||
const CAttrs=damageEvent.Attrs;
|
||
const TAttrs=TAttrsComp.Attrs;
|
||
let sConf = SkillSet[damageEvent.s_uuid];
|
||
if (!sConf) return 0;
|
||
if (this.debugMode) console.log(`[HeroAtkSystem] 伤害处理对象`,CAttrs,TAttrs);
|
||
// 2. 计算原始物理伤害和魔法伤害
|
||
// 物理伤害基础值 = 技能物理倍率 * (施法者物理攻击力 + 额外伤害) / 100 * 额外伤害比例
|
||
let apBase = (sConf.ap||0)*(CAttrs[Attrs.AP]+damageEvent.ext_dmg)/100*damageEvent.dmg_ratio;
|
||
// 魔法伤害基础值 = 技能魔法倍率 * (施法者魔法攻击力 + 额外伤害) / 100 * 额外伤害比例
|
||
let mapBase = (sConf.map||0)*(CAttrs[Attrs.MAP]+damageEvent.ext_dmg)/100*damageEvent.dmg_ratio;
|
||
if (this.debugMode) console.log(`[HeroAtkSystem] 物理伤害基础值: ${apBase}, 技能ap=${sConf.ap},施法者物理攻击力: ${CAttrs[Attrs.AP]}, 魔法伤害基础值: ${mapBase}技能map=${sConf.map}
|
||
额外伤害:${damageEvent.ext_dmg}, 额外伤害比例:${damageEvent.dmg_ratio}`);
|
||
|
||
// 3. 获取目标防御属性
|
||
const def = (TAttrs[Attrs.DEF]||0); // 目标物理防御
|
||
const mdef = (TAttrs[Attrs.MDEF]||0); // 目标魔法防御
|
||
|
||
// 4. 计算防御减免系数(采用公式:防御/(防御+常数),确保防御值不会导致伤害减到0)
|
||
const apRed = def / (def + FightSet.DEF_C); // 物理防御减免系数
|
||
const mapRed = mdef / (mdef + FightSet.MDEF_C); // 魔法防御减免系数
|
||
|
||
// 5. 应用防御减免到基础伤害,向下取整
|
||
let apAfter = Math.floor(apBase * (1 - apRed)); // 物理伤害 - 防御减免
|
||
let mapAfter = Math.floor(mapBase * (1 - mapRed)); // 魔法伤害 - 防御减免
|
||
|
||
if (this.debugMode) console.log(`[HeroAtkSystem] 物理伤害基础值: ${apBase}, 物理伤害 - 防御减免: ${apAfter} 魔法伤害基础值: ${mapBase}, 魔法伤害 - 防御减免: ${mapAfter}`);
|
||
|
||
// 6. 应用物理/魔法攻击力和抗性修正
|
||
// 物理伤害修正:基础伤害 * (1 + 物理攻击力加成%) * (1 - 目标物理抗性%)
|
||
apAfter = this.applyPR(apAfter, CAttrs[Attrs.PHYS_POWER]||0, TAttrs[Attrs.PHYS_RES]||0);
|
||
// 魔法伤害修正:基础伤害 * (1 + 魔法攻击力加成%) * (1 - 目标魔法抗性%)
|
||
mapAfter = this.applyPR(mapAfter, CAttrs[Attrs.MAGIC_POWER]||0, TAttrs[Attrs.MAGIC_RES]||0);
|
||
if (this.debugMode) console.log(`[HeroAtkSystem] 物理伤害修正后: ${apAfter} 魔法伤害修正后: ${mapAfter} `);
|
||
|
||
// 7. 根据技能元素类型,应用元素属性加成和抗性修正
|
||
switch (sConf.DType) {
|
||
case DType.ICE:
|
||
// 冰系伤害修正:魔法伤害 * (1 + 冰系攻击力加成%) * (1 - 目标冰系抗性%)
|
||
mapAfter = this.applyPR(mapAfter, CAttrs[Attrs.ICE_POWER]||0, TAttrs[Attrs.ICE_RES]||0);
|
||
break;
|
||
case DType.FIRE:
|
||
// 火系伤害修正:魔法伤害 * (1 + 火系攻击力加成%) * (1 - 目标火系抗性%)
|
||
mapAfter = this.applyPR(mapAfter, CAttrs[Attrs.FIRE_POWER]||0, TAttrs[Attrs.FIRE_RES]||0);
|
||
break;
|
||
case DType.WIND:
|
||
// 风系伤害修正:魔法伤害 * (1 + 风系攻击力加成%) * (1 - 目标风系抗性%)
|
||
mapAfter = this.applyPR(mapAfter, CAttrs[Attrs.WIND_POWER]||0, TAttrs[Attrs.WIND_RES]||0);
|
||
break;
|
||
}
|
||
if (this.debugMode) console.log(`[HeroAtkSystem] 物理伤害修正后: ${apAfter} 元素伤害修正后: ${mapAfter}`);
|
||
|
||
// 8. 计算最终总伤害(物理伤害 + 魔法伤害)
|
||
let total = apAfter + mapAfter;
|
||
//9.1 易伤
|
||
let DMG_INVUL = TAttrs[Attrs.DMG_INVUL]||0
|
||
//9.2 免伤 属性免伤+天赋免伤
|
||
let DMG_RED =TAttrs[Attrs.DMG_RED]||0+TAttrsComp.useCountValTal(Attrs.DMG_RED);
|
||
// 10. 确保伤害值非负,返回最终伤害
|
||
total = Math.max(0,total);
|
||
//11. 易伤减免 免伤属性免伤+天赋免伤
|
||
total = Math.floor(total * (1 + ((DMG_INVUL-DMG_RED)/100)));
|
||
if (this.debugMode) console.log(`[HeroAtkSystem] 易伤减免后: ${total}`);
|
||
return Math.max(0,total);
|
||
}
|
||
|
||
/**
|
||
* 应用攻击力加成和抗性减免的通用计算方法
|
||
*
|
||
* 这是一个核心的伤害修正计算方法,用于处理各种类型的攻击力加成和抗性减免。
|
||
* 该方法采用乘法叠加的方式,分别应用攻击者的加成属性和目标的抗性属性。
|
||
*
|
||
* 计算公式:
|
||
* 最终值 = 基础值 × (1 + 攻击力加成% / 100) × (1 - 抗性% / 100)
|
||
*
|
||
* 适用场景:
|
||
* - 物理攻击力加成和物理抗性减免
|
||
* - 魔法攻击力加成和魔法抗性减免
|
||
* - 元素攻击力加成和元素抗性减免
|
||
* - 其他需要同时考虑加成和减免的属性修正
|
||
*
|
||
* 计算逻辑说明:
|
||
* 1. 首先将百分比转换为小数形式(除以100)
|
||
* 2. 应用攻击者的加成:基础值 × (1 + 加成系数)
|
||
* 3. 应用目标的抗性:上一步结果 × (1 - 抗性系数)
|
||
* 4. 向下取整,确保结果为整数
|
||
*
|
||
* @param base 基础值(通常是经过防御减免后的伤害值)
|
||
* @param power 攻击者的攻击力加成值(百分比形式,如50表示50%)
|
||
* @param res 目标的抗性值(百分比形式,如30表示30%)
|
||
* @returns 经过攻击力加成和抗性减免后的最终值(向下取整)
|
||
*
|
||
* @important 注意事项:
|
||
* - 当抗性值大于100时,可能导致最终值为负数或零
|
||
* - 所有计算结果会向下取整,确保游戏中的数值为整数
|
||
* - 此方法可以被多次调用,以叠加不同类型的加成和减免
|
||
*/
|
||
private applyPR(base: number, power: number, res: number): number {
|
||
// 计算公式:基础值 × (1 + 攻击力加成%) × (1 - 抗性%)
|
||
// 1. 将百分比转换为小数:power/100 和 res/100
|
||
// 2. 应用攻击力加成:1 + (power/100)
|
||
// 3. 应用抗性减免:1 - (res/100)
|
||
// 4. 最终计算并向下取整
|
||
return Math.floor(base * (1 + (power/100)) * (1 - (res/100)));
|
||
}
|
||
/**
|
||
* 处理角色死亡
|
||
*
|
||
* 死亡处理流程:
|
||
* 1. 标记死亡状态(is_dead = true)
|
||
* 2. 触发死亡事件(onDeath)
|
||
* 3. 记录调试信息(如启用调试模式)
|
||
*
|
||
* @param entity 死亡的实体
|
||
*
|
||
* @important 死亡状态一旦设置,该实体将不再处理新的伤害事件
|
||
* 这确保了死亡逻辑的单一性和一致性
|
||
*/
|
||
private doDead(entity: ecs.Entity): void {
|
||
const TAttrsComp = entity.get(HeroAttrsComp);
|
||
if (!TAttrsComp || TAttrsComp.is_dead) return;
|
||
|
||
TAttrsComp.is_dead = true;
|
||
|
||
// 触发死亡事件
|
||
this.onDeath(entity);
|
||
|
||
if (this.debugMode) {
|
||
console.log(`[HeroAtkSystem] ${TAttrsComp.hero_name} 死亡`);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* 统一概率判定方法
|
||
*
|
||
* 用于所有概率相关的判定:
|
||
* - 暴击率判定
|
||
* - 闪避率判定
|
||
* - 击退概率判定
|
||
* - 其他特殊效果概率
|
||
*
|
||
* @param rate 概率值(0-100的百分比)
|
||
* @returns true-判定成功,false-判定失败
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 10%概率触发
|
||
* if (this.checkChance(10)) {
|
||
* // 触发特殊效果
|
||
* }
|
||
* ```
|
||
*
|
||
* @important 概率为0或负数时直接返回false,避免不必要的随机数计算
|
||
*/
|
||
private checkChance(rate: number): boolean {
|
||
if (rate <= 0) return false;
|
||
const r = Math.random() * 100;
|
||
return r < rate;
|
||
}
|
||
|
||
|
||
/**
|
||
* 护盾吸收伤害
|
||
*
|
||
* 护盾吸收逻辑:
|
||
* 1. 如果护盾值 >= 伤害值:完全吸收,剩余伤害为0
|
||
* 2. 如果护盾值 < 伤害值:部分吸收,剩余伤害 = 原伤害 - 护盾值
|
||
* 3. 护盾被击破时,重置护盾最大值属性
|
||
*
|
||
* @param TAttrsComp 被攻击者的属性组件(包含当前护盾值)
|
||
* @param damage 原始伤害值
|
||
* @returns 剩余伤害值(护盾吸收后的结果)
|
||
*/
|
||
private absorbShield(TAttrsComp: HeroAttrsComp, damage: number): number {
|
||
if (TAttrsComp.shield <= 0) return damage;
|
||
|
||
if (TAttrsComp.shield >= damage) {
|
||
TAttrsComp.shield -= damage;
|
||
if (TAttrsComp.shield <= 0) {
|
||
TAttrsComp.shield = 0;
|
||
TAttrsComp.Attrs[Attrs.SHIELD_MAX] = 0;
|
||
}
|
||
return 0;
|
||
} else {
|
||
const remainingDamage = damage - TAttrsComp.shield;
|
||
TAttrsComp.shield = 0;
|
||
TAttrsComp.Attrs[Attrs.SHIELD_MAX] = 0;
|
||
return remainingDamage;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 被攻击时触发的事件
|
||
*
|
||
* 预留的扩展点,用于处理被攻击时的特殊逻辑:
|
||
* - 触发反伤效果(荆棘光环等)
|
||
* - 触发被攻击天赋(如受击回血、受击反击等)
|
||
* - 触发特殊状态(如受伤狂暴、受伤护盾等)
|
||
*
|
||
* @param entity 被攻击的实体
|
||
*
|
||
* @todo 当前对怪物实体直接返回,后续可以根据需求扩展怪物的被攻击逻辑
|
||
*/
|
||
private onAttacked(entity: ecs.Entity): void {
|
||
const TAttrsComp = entity.get(HeroAttrsComp);
|
||
if (!TAttrsComp || TAttrsComp.is_dead) return;
|
||
TAttrsComp.atked_count++;
|
||
// 这里可以添加被攻击时的特殊处理逻辑
|
||
if (TAttrsComp.fac === FacSet.MON) return;
|
||
|
||
// 例如:触发某些天赋效果、反击逻辑等
|
||
}
|
||
|
||
/**
|
||
* 死亡时触发的事件
|
||
*
|
||
* 根据实体阵营类型处理不同的死亡逻辑:
|
||
*
|
||
* - FacSet.MON(怪物):触发掉落逻辑
|
||
* - 延迟执行掉落,避免阻塞主逻辑
|
||
* - 可以扩展:经验值计算、任务进度等
|
||
*
|
||
* - FacSet.HERO(英雄):触发英雄死亡特殊处理
|
||
* - 游戏结束判定
|
||
* - 复活机制检查
|
||
* - 死亡惩罚/奖励
|
||
*
|
||
* @param entity 死亡的实体
|
||
*
|
||
* @important 死亡事件应该幂等,避免重复触发
|
||
*/
|
||
private onDeath(entity: ecs.Entity): void {
|
||
const TAttrsComp = entity.get(HeroAttrsComp);
|
||
if (!TAttrsComp) return;
|
||
|
||
if (TAttrsComp.fac === FacSet.MON) {
|
||
// 怪物死亡处理
|
||
this.scheduleDrop(entity);
|
||
} else if (TAttrsComp.fac === FacSet.HERO) {
|
||
// 英雄死亡处理
|
||
this.scheduleHeroDeath(entity);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 延迟执行掉落逻辑
|
||
*
|
||
* 采用延迟执行的原因:
|
||
* 1. 避免在伤害计算过程中阻塞主线程
|
||
* 2. 给死亡动画播放留出时间
|
||
* 3. 可以批量处理多个掉落,优化性能
|
||
*
|
||
* @param entity 死亡的怪物实体
|
||
*
|
||
* @todo 具体实现可以包括:
|
||
* - 根据怪物等级计算基础掉落
|
||
* - 幸运值影响掉落品质
|
||
* - 特殊事件(双倍掉落、稀有掉落等)
|
||
* - 掉落物在场景中的生成位置计算
|
||
*/
|
||
private scheduleDrop(entity: ecs.Entity): void {
|
||
// 这里可以添加掉落逻辑
|
||
// 例如:延迟一段时间后生成掉落物品
|
||
}
|
||
|
||
/**
|
||
* 延迟执行英雄死亡逻辑
|
||
*
|
||
* 英雄死亡的特殊处理,比普通怪物复杂:
|
||
*
|
||
* 处理内容包括:
|
||
* - 检查复活次数和复活条件
|
||
* - 触发游戏结束界面(如适用)
|
||
* - 记录死亡统计信息
|
||
* - 处理死亡惩罚(经验损失、装备损坏等)
|
||
*
|
||
* @param entity 死亡的英雄实体
|
||
*
|
||
* @important 英雄死亡通常需要玩家交互,所以必须延迟处理
|
||
* 给玩家足够的反馈时间和操作空间
|
||
*/
|
||
private scheduleHeroDeath(entity: ecs.Entity): void {
|
||
// 这里可以添加英雄死亡的特殊处理
|
||
// 例如:触发游戏结束、复活机制等
|
||
}
|
||
|
||
/**
|
||
* 启用调试模式
|
||
*/
|
||
enableDebug() {
|
||
this.debugMode = true;
|
||
}
|
||
|
||
/**
|
||
* 禁用调试模式
|
||
*/
|
||
disableDebug() {
|
||
this.debugMode = false;
|
||
}
|
||
|
||
|
||
} |