import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { Attrs } from "../common/config/HeroAttrs"; import { HeroInfo } from "../common/config/heroSet"; import { HSSet, SkillSet } from "../common/config/SkillSet"; import { HeroAttrsComp } from "./HeroAttrsComp"; /** * ==================== 技能槽位数据 ==================== * 单个技能的运行时数据 */ export interface SkillSlot { s_uuid: number; // 技能配置ID cd: number; // 当前CD时间(递减) cd_max: number; // 最大CD时间 cost: number; // MP消耗 level: number; // 技能等级(预留) dis: number; // 攻击距离 hset: HSSet; // 技能设定, 0:普通攻击, 1:一般技能, 2:必杀技 } /** * ==================== 英雄技能数据组件 ==================== * * 职责: * 1. 存储角色拥有的技能列表 * 2. 管理技能CD状态 * 3. 提供技能查询接口 * * 设计理念: * - 只存数据,不含施法逻辑 * - CD 更新由 HSkillSystem 负责 * - 施法判定由 HSkillSystem 负责 */ @ecs.register('HeroSkills') export class HeroSkillsComp extends ecs.Comp { // ==================== 技能槽位列表 ==================== /** 技能槽位数组(最多4个技能) */ skills: Record = {}; // ==================== 辅助方法 ==================== /** * 初始化技能列表 * @param sUuids 技能配置ID数组 * @param uuid 英雄UUID * @param entity 实体对象(用于更新技能距离缓存) */ initSkills(sUuids: number[], uuid: number, entity?: ecs.Entity) { this.skills = []; for (let i = 0; i < sUuids.length; i++) { const s_uuid = sUuids[i]; const config = SkillSet[s_uuid]; if (!config) { console.warn(`[HeroSkills] 技能配置不存在: ${s_uuid}`); continue; } // 第0个技能的 cd_max 取 herosinfo[uuid].as const cdMax = i === 0 ? HeroInfo[uuid].as : config.cd; let hset = HSSet.atk; if(i ===1) hset = HSSet.skill; if(i ===2) hset = HSSet.max; this.skills[s_uuid] = { s_uuid: config.uuid, cd: 0, cd_max: cdMax, cost: config.cost, level: 1, dis: Number(config.dis), hset: hset, }; } // 更新技能距离缓存 if (entity) { const attrsComp = entity.get(HeroAttrsComp); if (attrsComp) { attrsComp.updateSkillDistanceCache(this); } } } /** * 添加单个技能 * @param s_uuid 技能配置ID * @param entity 实体对象(用于更新技能距离缓存) */ addSkill(s_uuid: number, entity?: ecs.Entity, hset: HSSet=HSSet.skill) { const config = SkillSet[s_uuid]; if (!config) { console.warn(`[HeroSkills] 技能配置不存在: ${s_uuid}`); return; } this.skills[s_uuid] = { s_uuid: config.uuid, cd: 0, cd_max: config.cd, cost: config.cost, level: 1, dis: Number(config.dis), hset: hset, }; // 更新技能距离缓存 if (entity) { const attrsComp = entity.get(HeroAttrsComp); if (attrsComp) { attrsComp.updateSkillDistanceCache(this); } } } /** * 获取指定s_uuid的技能 */ getSkill(s_uuid: number): SkillSlot | null { return this.skills[s_uuid] ?? null; } /** * 检查技能是否可施放(通过s_uuid) * @param s_uuid 技能配置ID * @param currentMp 当前MP值 */ canCast(s_uuid: number, currentMp: number): boolean { const skill = this.getSkill(s_uuid); if (!skill) return false; // 检查CD和MP return skill.cd <= 0 && currentMp >= skill.cost; } /** * 重置技能CD(开始冷却,通过索引) */ resetCD(s_uuid: number) { let attrsCom = this.ent.get(HeroAttrsComp); if (!attrsCom) return; const skill = this.getSkill(s_uuid); if (!skill) return; // 普通攻击(skills[0])受 AS 影响,其余技能受 SS 影响 const isNormalAttack = s_uuid === this.skills[0]?.s_uuid; const speedAttr = isNormalAttack ? Attrs.AS : Attrs.SS; const speedBonus = attrsCom.Attrs[speedAttr] / 100; // 100 表示 100% 提速 const speedMultiplier = 1 / (1 + speedBonus); // 提速 100% => cd 减半 skill.cd = skill.cd_max * speedMultiplier; if (skill) { skill.cd = skill.cd_max; } } /** * 更新所有技能CD(每帧调用) * @param dt 时间增量 */ updateCDs(dt: number) { for (const s_uuid in this.skills) { const skill = this.skills[Number(s_uuid)]; if (skill.cd > 0) { skill.cd -= dt; if (skill.cd < 0) { skill.cd = 0; } } } } /** * 获取所有可施放的技能索引 */ getReadySkills(currentMp: number): number[] { const ready: number[] = []; for (const s_uuid in this.skills) { if (this.canCast(Number(s_uuid), currentMp)) { ready.push(Number(s_uuid)); } } return ready; } /** * 检查技能攻击距离是否足够 * @param s_uuid 技能配置ID * @param distance 目标距离 * @returns 是否在攻击范围内 */ canReachTarget(s_uuid: number, distance: number): boolean { const skill = this.getSkill(s_uuid); if (!skill) { return false; } return distance <= skill.dis; } /** * 获取技能的攻击距离 * @param s_uuid 技能配置ID * @returns 攻击距离,如果技能不存在返回0 */ getSkillDistance(s_uuid: number): number { const skill = this.getSkill(s_uuid); return skill ? skill.dis : 0; } /** * 获取可施放技能中的最远攻击距离 * @param mp 当前MP值 * @returns 最远攻击距离,如果没有可用技能返回0 */ getMaxSkillDistance(mp: number): number { const readySkills = this.getReadySkills(mp); if (readySkills.length === 0) return 0; let maxDistance = 0; for (const s_uuid of readySkills) { const skill = this.getSkill(s_uuid); if (skill && skill.dis > maxDistance) { maxDistance = skill.dis; } } return maxDistance; } /** * 获取可施放技能中的最近攻击距离 * @param mp 当前MP值 * @returns 最近攻击距离,如果没有可用技能返回0 */ getMinSkillDistance(mp: number): number { const readySkills = this.getReadySkills(mp); if (readySkills.length === 0) return 0; let minDistance = Number.MAX_VALUE; for (const s_uuid of readySkills) { const skill = this.getSkill(s_uuid); if (skill && skill.dis < minDistance) { minDistance = skill.dis; } } return minDistance === Number.MAX_VALUE ? 0 : minDistance; } /** * 获取所有技能中的最小攻击距离(不考虑MP限制) * 用于移动停止判断,让英雄在合适位置等待回蓝 * @returns 最小攻击距离,如果没有技能返回0 */ getAbsoluteMinSkillDistance(): number { const skillIds = Object.keys(this.skills).map(Number); if (skillIds.length === 0) return 0; let minDistance = Number.MAX_VALUE; for (const s_uuid of skillIds) { const skill = this.getSkill(s_uuid); if (skill && skill.dis < minDistance) { minDistance = skill.dis; } } return minDistance === Number.MAX_VALUE ? 0 : minDistance; } reset() { this.skills = {}; } }