import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { Attrs, AttrsType, BType, NeAttrs } from "../common/config/HeroAttrs"; import { BuffConf, SkillSet } from "../common/config/SkillSet"; import { HeroInfo, AttrSet, HeroUpSet } from "../common/config/heroSet"; import { MonModelComp } from "./MonModelComp"; import { FacSet } from "../common/config/BoxSet"; import { FightSet } from "../common/config/Mission"; @ecs.register('HeroModel') export class HeroModelComp extends ecs.Comp { // ==================== 角色基础信息 ==================== hero_uuid: number = 1001; hero_name: string = "hero"; lv: number = 1; type: number = 0; // 0近战 1远程 2辅助 fac: number = 0; // 0:hero 1:monster // ==================== 基础属性(有初始值) ==================== base_ap: number = 0; // 基础攻击 base_map: number = 0; // 基础魔法攻击 base_def: number = 5; // 基础防御 base_hp: number = 100; // 基础血量 base_mp: number = 100; // 基础魔法值 base_speed: number = 100; // 基础移动速度 base_dis: number = 100; // 基础距离 // ==================== 动态属性值 ==================== hp: number = 100; // 当前血量 mp: number = 100; // 当前魔法值 shield: number = 0; // 当前护盾 Attrs: any = []; // 最终属性数组(经过Buff计算后) NeAttrs: any = []; // 负面状态数组 // ==================== Buff/Debuff 系统 ==================== /** 持久型buff数组 - 不会自动过期 */ BUFFS: Record> = {}; /** 临时型buff数组 - 按时间自动过期 */ BUFFS_TEMP: Record> = {}; // ==================== 标记状态 ==================== is_dead: boolean = false; is_count_dead: boolean = false; is_boss: boolean = false; is_big_boss: boolean = false; is_master: boolean = false; is_friend: boolean = false; is_kalami: boolean = false; // ==================== 计数统计 ==================== atk_count: number = 0; // 攻击次数 atked_count: number = 0; // 被攻击次数 // ==================== 技能配置 ==================== skills: any = []; // ==================== BUFF 系统初始化 ==================== /** * 初始化角色的 buff debuff * 从 HeroInfo 读取初始配置,建立属性系统 */ initAttrs() { // 清空现有 buff/debuff this.BUFFS = {}; this.BUFFS_TEMP = {}; // 获取英雄配置 const heroInfo = HeroInfo[this.hero_uuid]; if (!heroInfo) return; // 1. 重置为基础值 this.Attrs[Attrs.HP_MAX] = this.base_hp; this.Attrs[Attrs.MP_MAX] = this.base_mp; this.Attrs[Attrs.DEF] = this.base_def; this.Attrs[Attrs.AP] = this.base_ap; this.Attrs[Attrs.MAP] = this.base_map; this.Attrs[Attrs.SPEED] = this.base_speed; this.Attrs[Attrs.DIS] = this.base_dis; // 2. 初始化其他属性(无初始值的) for (const attrKey in this.Attrs) { const attrIndex = parseInt(attrKey); if ( attrIndex !== Attrs.HP_MAX && attrIndex !== Attrs.MP_MAX && attrIndex !== Attrs.DEF && attrIndex !== Attrs.AP && attrIndex !== Attrs.MAP && attrIndex !== Attrs.SPEED && attrIndex !== Attrs.DIS ) { this.Attrs[attrIndex] = 0; } } // 加载初始 buff if (heroInfo.buff && heroInfo.buff.length > 0) { for (const buffConf of heroInfo.buff) { this.addBuff(buffConf); } } } // ==================== BUFF 管理 ==================== /** * 添加 buff 效果(支持多次叠加) * @param buffConf buff 配置 (来自 SkillSet.BuffConf heroSet.buff) */ addBuff(buffConf: BuffConf) { const isPermanent = buffConf.time === 0; const attrIndex = buffConf.buff; if (isPermanent) { // 添加持久buff到BUFFS - 直接追加到数组 if (!this.BUFFS[attrIndex]) { this.BUFFS[attrIndex] = []; } this.BUFFS[attrIndex].push({ value: buffConf.value, BType: buffConf.BType }); } else { // 添加临时buff到BUFFS_TEMP - 直接追加到数组 if (!this.BUFFS_TEMP[attrIndex]) { this.BUFFS_TEMP[attrIndex] = []; } this.BUFFS_TEMP[attrIndex].push({ value: buffConf.value, BType: buffConf.BType, remainTime: buffConf.time }); } // 重新计算受影响的属性 this.recalculateSingleAttr(attrIndex); } // ==================== 属性计算系统 ==================== /** * 重新计算单个属性 * @param attrIndex 属性索引 */ recalculateSingleAttr(attrIndex: number) { // 1. 获取基础值 const baseValues: Record = { [Attrs.HP_MAX]: this.base_hp, [Attrs.MP_MAX]: this.base_mp, [Attrs.DEF]: this.base_def, [Attrs.AP]: this.base_ap, [Attrs.MAP]: this.base_map, [Attrs.SPEED]: this.base_speed, [Attrs.SHIELD_MAX]: 0 }; const baseVal = baseValues[attrIndex] !== undefined ? baseValues[attrIndex] : 0; // 2. 收集所有数值型 buff/debuff let totalValue = baseVal; // 遍历持久buff数组 if (this.BUFFS[attrIndex] && this.BUFFS[attrIndex].length > 0) { for (const buff of this.BUFFS[attrIndex]) { if (buff.BType === BType.VALUE) { totalValue += buff.value; } } } // 遍历临时buff数组 if (this.BUFFS_TEMP[attrIndex] && this.BUFFS_TEMP[attrIndex].length > 0) { for (const buff of this.BUFFS_TEMP[attrIndex]) { if (buff.BType === BType.VALUE) { totalValue += buff.value; } } } // 3. 收集所有百分比型 buff/debuff let totalRatio = 0; // 遍历持久buff数组 if (this.BUFFS[attrIndex] && this.BUFFS[attrIndex].length > 0) { for (const buff of this.BUFFS[attrIndex]) { if (buff.BType === BType.RATIO) { totalRatio += buff.value; } } } // 遍历临时buff数组 if (this.BUFFS_TEMP[attrIndex] && this.BUFFS_TEMP[attrIndex].length > 0) { for (const buff of this.BUFFS_TEMP[attrIndex]) { if (buff.BType === BType.RATIO) { totalRatio += buff.value; } } } // 4. 根据属性类型计算最终值 const attrType = AttrsType[attrIndex]; const isRatioAttr = attrType === BType.RATIO; if (isRatioAttr) { // 百分比型属性:直接加减 this.Attrs[attrIndex] = totalValue + totalRatio; } else { // 数值型属性:(基础值+数值) × (1 + 百分比/100) this.Attrs[attrIndex] = Math.floor(totalValue * (1 + totalRatio / 100)); } // 5. 确保属性值合理 this.clampSingleAttr(attrIndex); } /** * 确保单个属性值合理 */ private clampSingleAttr(attrIndex: number) { switch(attrIndex) { case Attrs.HP_MAX: case Attrs.MP_MAX: this.Attrs[attrIndex] = Math.max(1, this.Attrs[attrIndex]); break; case Attrs.DEF: case Attrs.AP: case Attrs.MAP: this.Attrs[attrIndex] = Math.max(1, this.Attrs[attrIndex]); break; case Attrs.CRITICAL: case Attrs.DODGE: case Attrs.HIT: this.Attrs[attrIndex] = Math.max(0, Math.min(AttrSet.ATTR_MAX, this.Attrs[attrIndex])); break; } } // ==================== 临时 BUFF/DEBUFF 更新 ==================== /** * 更新临时 buff/debuff 的剩余时间 * @param dt 时间增量 */ updateTemporaryBuffsDebuffs(dt: number) { const affectedAttrs = new Set(); // 更新临时型buff for (const attrIndex in this.BUFFS_TEMP) { const buffs = this.BUFFS_TEMP[attrIndex]; buffs.forEach(buff => { buff.remainTime -= dt; if (buff.remainTime <= 0) { const index = buffs.indexOf(buff); if (index > -1) { buffs.splice(index, 1); } } }); if (buffs.length === 0) { delete this.BUFFS_TEMP[attrIndex]; affectedAttrs.add(parseInt(attrIndex)); } } // 负面状态更新 for (const key in this.NeAttrs) { const debuff = this.NeAttrs[key]; debuff.remainTime -= dt; if (debuff.remainTime <= 0) { debuff.remainTime = 0; } } // 只重新计算受影响的属性 affectedAttrs.forEach(attrIndex => { this.recalculateSingleAttr(attrIndex); }); } // ==================== BUFF 辅助方法 ==================== /** * 清空buff * @param attrIndex 属性索引,如果为空则清理所有buff * @param isBuff true时清理value>0的增益buff,false时清理value<0的减益buff */ clearBuffs(attrIndex?: number, isBuff: boolean = true): void { if (attrIndex === undefined) { for (const attrIndex in this.BUFFS_TEMP) { this.clearBuffsForAttr(parseInt(attrIndex), isBuff); } } else { this.clearBuffsForAttr(attrIndex, isBuff); } } /** * 清理指定属性的buff * @param attrIndex 属性索引 * @param isBuff true清理增益buff,false清理减益buff */ private clearBuffsForAttr(attrIndex: number, isBuff: boolean): void { const buffContainer = this.BUFFS_TEMP; if (!buffContainer[attrIndex]) return; buffContainer[attrIndex] = buffContainer[attrIndex].filter(buff => { const shouldClear = isBuff ? buff.value > 0 : buff.value < 0; return !shouldClear; }); if (buffContainer[attrIndex].length === 0) { delete buffContainer[attrIndex]; } this.recalculateSingleAttr(attrIndex); } // ==================== NeAttrs(负面状态)管理 ==================== /** * 清理单个NeAttr(负面状态) */ clearNeAttr(neAttrIndex: number): void { if (this.NeAttrs[neAttrIndex]) { this.NeAttrs[neAttrIndex].value = 0; this.NeAttrs[neAttrIndex].time = 0; } } /** * 清理所有NeAttrs(负面状态) */ clearAllNeAttrs(): void { for (const key in this.NeAttrs) { this.NeAttrs[key].value = 0; this.NeAttrs[key].time = 0; } } /** * 检查是否处于眩晕状态 */ public isStun(): boolean { return this.NeAttrs[NeAttrs.IN_STUN]?.time > 0; } /** * 检查是否处于冰冻状态 */ public isFrost(): boolean { return this.NeAttrs[NeAttrs.IN_FROST]?.time > 0; } reset() { // 重置为初始状态 this.hero_uuid = 1001; this.hero_name = "hero"; this.lv = 1; this.type = 0; this.fac = 0; this.base_ap = 0; this.base_map = 0; this.base_def = 5; this.base_hp = 100; this.base_mp = 100; this.base_speed = 100; this.base_dis = 100; this.hp = 100; this.mp = 100; this.shield = 0; this.Attrs = []; this.NeAttrs = []; this.BUFFS = {}; this.BUFFS_TEMP = {}; this.is_dead = false; this.is_count_dead = false; this.is_boss = false; this.is_big_boss = false; this.is_master = false; this.is_friend = false; this.is_kalami = false; this.atk_count = 0; this.atked_count = 0; this.skills = []; } } /** * ==================== 英雄属性更新系统 ==================== * * 按照 ECS 设计理念: * - Component(HeroModelComp):存储数据 * - System(HeroAttrSystem):处理业务逻辑 * * 系统职责: * 1. 每帧更新临时 Buff(时间递减,过期移除) * 2. 每帧更新 HP/MP 自然回复 * 3. 限制属性值在合理范围内 * * 使用方式: * 在 RootSystem 中注册此系统,它会自动每帧更新所有拥有 HeroModelComp 的实体 */ export class HeroAttrSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate, ecs.IEntityEnterSystem, ecs.ISystemFirstUpdate { // ==================== 调试统计(可选)==================== private entityCount: number = 0; // 本帧处理的实体数 private frameCount: number = 0; // 总帧数 private debugMode: boolean = false; // 是否启用调试模式 /** * 过滤器:只处理拥有 HeroModelComp 的实体 */ filter(): ecs.IMatcher { return ecs.allOf(HeroModelComp); } /** * 实体首次进入系统时调用(每个实体只调用一次) */ entityEnter(e: ecs.Entity): void { const model = e.get(HeroModelComp); if (!model) return; console.log(`[HeroAttrSystem] 英雄进入系统: ${model.hero_name} (uuid: ${model.hero_uuid})`); } /** * 系统首次更新前调用(整个系统只调用一次) */ firstUpdate(): void { console.log("[HeroAttrSystem] 系统首次更新"); } /** * 每帧更新(为每个英雄调用一次) * * ⭐ 关键理解: * - 如果有 3 个英雄,这个方法每帧会被调用 3 次 * - 每次调用处理不同的实体 e * - 这是正确的设计,不是 bug */ update(e: ecs.Entity): void { const model = e.get(HeroModelComp); if (!model || model.is_dead) return; // 统计:记录本帧处理的实体数 this.entityCount++; // 调试日志(可选,调试时启用) if (this.debugMode) { console.log(` [${this.entityCount}] 更新英雄: ${model.hero_name}, HP: ${model.hp.toFixed(2)}`); } // 1. 更新临时 Buff/Debuff(时间递减,过期自动移除) model.updateTemporaryBuffsDebuffs(this.dt); // 2. HP/MP 自然回复(业务规则) model.mp += HeroUpSet.MP * this.dt; model.hp += HeroUpSet.HP * this.dt; // 3. 限制属性值在合理范围内 if (model.mp > model.Attrs[Attrs.MP_MAX]) { model.mp = model.Attrs[Attrs.MP_MAX]; } if (model.hp > model.Attrs[Attrs.HP_MAX]) { model.hp = model.Attrs[Attrs.HP_MAX]; } // 每 60 帧输出一次统计 this.frameCount++; if (this.frameCount % 60 === 0 && this.entityCount === 1) { console.log(`[HeroAttrSystem] 第 ${this.frameCount} 帧,处理 ${this.entityCount} 个英雄`); } // 注意:显示更新由 HeroViewComp 负责,这里只处理数据 } /** * 启用调试模式(调试时使用) */ enableDebug() { this.debugMode = true; } /** * 禁用调试模式(正式运行) */ disableDebug() { this.debugMode = false; } } /** * ==================== 英雄战斗系统 ==================== * * 按照 ECS 设计理念: * - Component(HeroModelComp):存储数据 * - System(HeroBattleSystem):处理战斗业务逻辑 * * 系统职责: * 1. 处理角色被攻击逻辑(伤害计算、暴击判定、闪避判定) * 2. 处理护盾吸收逻辑 * 3. 处理角色死亡逻辑 * 4. 处理战斗状态管理 * * 使用方式: * 在 RootSystem 中注册此系统,它会自动处理所有拥有 HeroModelComp 的实体的战斗逻辑 */ export class HeroBattleSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate, ecs.IEntityEnterSystem { private debugMode: boolean = false; // 是否启用调试模式 /** * 过滤器:只处理拥有 HeroModelComp 的实体 */ filter(): ecs.IMatcher { return ecs.allOf(HeroModelComp); } /** * 实体首次进入系统时调用(每个实体只调用一次) */ entityEnter(e: ecs.Entity): void { const model = e.get(HeroModelComp); if (!model) return; console.log(`[HeroBattleSystem] 英雄进入战斗系统: ${model.hero_name} (uuid: ${model.hero_uuid})`); } /** * 处理角色被攻击 * @param target 被攻击的目标实体 * @param remainingDamage 基础伤害值 * @param attackerAttrs 攻击者的属性 * @param skillId 技能ID * @returns 实际造成的伤害 */ public doAttack(target: ecs.Entity, remainingDamage: number, attackerAttrs: any, skillId: number): number { const targetModel = target.get(HeroModelComp); if (!targetModel || targetModel.is_dead) return 0; // 获取技能配置 const skillConf = SkillSet[skillId]; // 触发被攻击事件 this.onAttacked(target); // 闪避判定 if (this.checkDodge(targetModel)) { return 0; } // 暴击判定 const isCrit = this.checkCrit(attackerAttrs[Attrs.CRITICAL]); let damage = remainingDamage; if (isCrit) { damage = Math.floor(damage * (1 + (FightSet.CRIT_DAMAGE + attackerAttrs[Attrs.CRITICAL_DMG]) / 100)); } // 伤害计算(考虑易伤等debuff) damage = this.calculateDamage(targetModel, damage); // 护盾吸收 damage = this.absorbShield(targetModel, damage); if (damage <= 0) return 0; // 应用伤害 targetModel.hp -= damage; targetModel.atked_count++; // 检查死亡 if (targetModel.hp <= 0) { this.doDead(target); } if (this.debugMode) { console.log(`[HeroBattleSystem] ${targetModel.hero_name} 受到 ${damage} 点伤害 (暴击: ${isCrit})`); } return damage; } /** * 处理角色死亡 */ private doDead(entity: ecs.Entity): void { const model = entity.get(HeroModelComp); if (!model || model.is_dead) return; model.is_dead = true; // 触发死亡事件 this.onDeath(entity); if (this.debugMode) { console.log(`[HeroBattleSystem] ${model.hero_name} 死亡`); } } /** * 闪避判定 */ private checkDodge(model: HeroModelComp): boolean { if (model.Attrs[Attrs.DODGE] > 0) { const random = Math.random() * 100; if (random < model.Attrs[Attrs.DODGE]) { if (this.debugMode) { console.log(`[HeroBattleSystem] ${model.hero_name} 闪避了攻击`); } return true; } } return false; } /** * 暴击判定 */ private checkCrit(critRate: number): boolean { if (critRate > 0) { const random = Math.random() * 100; return random < critRate; } return false; } /** * 伤害计算(考虑易伤等debuff) */ private calculateDamage(model: HeroModelComp, baseDamage: number): number { // 这里可以添加易伤等debuff的计算逻辑 // 例如:如果目标有易伤buff,增加受到的伤害 return baseDamage; } /** * 护盾吸收伤害 */ private absorbShield(model: HeroModelComp, damage: number): number { if (model.shield <= 0) return damage; if (model.shield >= damage) { model.shield -= damage; if (model.shield <= 0) { model.shield = 0; model.Attrs[Attrs.SHIELD_MAX] = 0; } return 0; } else { const remainingDamage = damage - model.shield; model.shield = 0; model.Attrs[Attrs.SHIELD_MAX] = 0; return remainingDamage; } } /** * 被攻击时触发的事件 */ private onAttacked(entity: ecs.Entity): void { const model = entity.get(HeroModelComp); if (!model || model.is_dead) return; // 这里可以添加被攻击时的特殊处理逻辑 if (model.fac === FacSet.MON) return; // 例如:触发某些天赋效果、反击逻辑等 } /** * 死亡时触发的事件 */ private onDeath(entity: ecs.Entity): void { const model = entity.get(HeroModelComp); if (!model) return; if (model.fac === FacSet.MON) { // 怪物死亡处理 this.scheduleDrop(entity); } else if (model.fac === FacSet.HERO) { // 英雄死亡处理 this.scheduleHeroDeath(entity); } } /** * 延迟执行掉落逻辑 */ private scheduleDrop(entity: ecs.Entity): void { // 这里可以添加掉落逻辑 // 例如:延迟一段时间后生成掉落物品 } /** * 延迟执行英雄死亡逻辑 */ private scheduleHeroDeath(entity: ecs.Entity): void { // 这里可以添加英雄死亡的特殊处理 // 例如:触发游戏结束、复活机制等 } /** * 启用调试模式 */ enableDebug() { this.debugMode = true; } /** * 禁用调试模式 */ disableDebug() { this.debugMode = false; } /** * 系统更新(每帧调用) * 可以在这里添加需要每帧处理的战斗逻辑 */ update(e: ecs.Entity): void { // 这里可以添加需要每帧处理的战斗逻辑 // 例如:持续伤害、战斗状态检查等 } }