import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { Vec3, v3 } from "cc"; import { HeroAttrsComp } from "./HeroAttrsComp"; import { HeroViewComp } from "./HeroViewComp"; import { SkillSet, SType } from "../common/config/SkillSet"; import { HeroSkillsComp, SkillSlot } from "./HeroSkills"; import { Skill } from "../skill/Skill"; /** * ==================== 自动施法系统 ==================== * * 职责: * 1. 检测可施放的技能 * 2. 根据策略自动施法(AI) * 3. 选择目标 * 4. 添加施法请求标记 * * 设计理念: * - 负责"何时施法"的决策 * - 通过添加 CSRequestComp 触发施法 * - 可被玩家输入系统或AI系统复用 * - 支持多种AI策略 */ @ecs.register('SACastSystem') export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { filter(): ecs.IMatcher { return ecs.allOf(HeroSkillsComp, HeroAttrsComp, HeroViewComp); } update(e: ecs.Entity): void { const skills = e.get(HeroSkillsComp); const heroAttrs = e.get(HeroAttrsComp); const heroView = e.get(HeroViewComp); if (!skills || !heroAttrs || !heroView) return; // 检查基本条件 if (heroAttrs.is_dead || heroAttrs.isStun() || heroAttrs.isFrost()) return; // 检查是否正在攻击(只有攻击时才释放技能) if (!heroAttrs.is_atking) return; // 获取所有可施放的技能 const readySkills = skills.getReadySkills(heroAttrs.mp); if (readySkills.length === 0) return; // 选择第一个可施放的伤害技能 for (const s_uuid of readySkills) { const skill = skills.getSkill(s_uuid); if (!skill) continue; const config = SkillSet[skill.s_uuid]; if (!config || config.SType !== SType.damage) continue; // ✅ 开始执行施法 this.startCast(e,skill); // 一次只施放一个技能 break; } } private startCast(e: ecs.Entity,skill:SkillSlot): void { if (!skill||!e) return const skills = e.get(HeroSkillsComp); const heroAttrs = e.get(HeroAttrsComp); const heroView = e.get(HeroViewComp); // 3. 检查施法条件 if (!this.checkCastConditions(skills, heroAttrs, skill.s_uuid)) return // 4. 执行施法 this.executeCast(e, skill.s_uuid, heroView); // 5. 扣除资源和重置CD heroAttrs.mp -= skill.cost; skills.resetCD(skill.s_uuid); } /** * 检查施法条件 */ private checkCastConditions(skills: HeroSkillsComp, heroAttrs: HeroAttrsComp, s_uuid: number): boolean { // 检查角色状态 if (heroAttrs.is_dead) { return false; } // 检查控制状态(眩晕、冰冻) if (heroAttrs.isStun() || heroAttrs.isFrost()) { return false; } // 检查CD和MP if (!skills.canCast(s_uuid, heroAttrs.mp)) { return false; } return true; } /** * 执行施法 */ private executeCast(casterEntity: ecs.Entity, s_uuid: number, heroView: HeroViewComp) { const config = SkillSet[s_uuid]; if (!config) { console.error("[SACastSystem] 技能配置不存在:", s_uuid); return; } // 1. 播放施法动画 heroView.playSkillEffect(s_uuid); // 2. 延迟创建技能实体(等待动画) const delay = config.with ?? 0.3; // 施法前摇时间 heroView.scheduleOnce(() => { this.createSkill(s_uuid, heroView); }, delay); const heroAttrs = casterEntity.get(HeroAttrsComp); // console.log(`[SACastSystem] ${heroAttrs?.hero_name ?? '未知'} 施放技能: ${config.name}`); } /** * 创建技能实体 */ private createSkill(s_uuid: number, caster: HeroViewComp) { // 检查节点有效性 if (!caster.node || !caster.node.isValid) { console.warn("[SACastSystem] 施法者节点无效"); return; } // 获取场景节点 const parent = caster.node.parent; if (!parent) { console.warn("[SACastSystem] 场景节点无效"); return; } // 获取目标位置 const targets = this.sTargets(caster); if (targets.length === 0) { console.warn("[SACastSystem] 没有找到有效目标"); return; } // 创建技能实体 const skill = ecs.getEntity(Skill); // 获取施法者位置作为起始位置 const startPos = caster.node.position.clone(); const targetPos = targets[0]; // 使用第一个目标位置 // console.log(`[SACastSystem]: ${s_uuid}, 起始位置: ${startPos}, 目标位置: ${targetPos}`); // 加载技能实体(包括预制体、组件初始化等) skill.load(startPos, parent, s_uuid, targetPos, caster); } /** * 选择目标位置 */ private sTargets(caster: HeroViewComp): Vec3[] { // 简化版:选择最前方的敌人 const targets: Vec3[] = []; // 这里可以调用 SkillConComp 的目标选择逻辑 // 暂时返回默认位置 const heroAttrs = caster.ent.get(HeroAttrsComp); const fac = heroAttrs?.fac ?? 0; const defaultX = fac === 0 ? 400 : -400; targets.push(v3(defaultX, 0, 0)); return targets; } /** * 选择伤害技能目标 */ private sDamageTargets(caster: HeroViewComp, config: any, maxTargets: number): Vec3[] { const targets: Vec3[] = []; const heroAttrs = caster.ent.get(HeroAttrsComp); if (!heroAttrs) return targets; // 寻找最近的敌人 const enemyPositions = this.findNearbyEnemies(caster, heroAttrs.fac, config.range || 300); // 选择最多maxTargets个目标 for (let i = 0; i < Math.min(maxTargets, enemyPositions.length); i++) { targets.push(enemyPositions[i]); } // 如果没有找到敌人,使用默认位置 if (targets.length === 0) { targets.push(...this.sDefaultTargets(caster, heroAttrs.fac)); } return targets; } /** * 选择治疗技能目标 */ private sHealTargets(caster: HeroViewComp, config: any, maxTargets: number): Vec3[] { const targets: Vec3[] = []; const heroAttrs = caster.ent.get(HeroAttrsComp); if (!heroAttrs) return targets; // 寻找血量最低的友军 const allyPositions = this.findLowHealthAllies(caster, heroAttrs.fac, config.range || 200); for (let i = 0; i < Math.min(maxTargets, allyPositions.length); i++) { targets.push(allyPositions[i]); } // 如果没有找到友军,治疗自己 if (targets.length === 0 && caster.node) { targets.push(caster.node.position.clone()); } return targets; } /** * 选择BUFF技能目标 */ private sBuffTargets(caster: HeroViewComp, config: any, maxTargets: number): Vec3[] { // BUFF技能通常施放在自己或友军身上 return this.sHealTargets(caster, config, maxTargets); } /** * 选择DEBUFF技能目标 */ private sDebuffTargets(caster: HeroViewComp, config: any, maxTargets: number): Vec3[] { // DEBUFF技能通常施放在敌人身上 return this.sDamageTargets(caster, config, maxTargets); } /** * 选择默认目标 */ private sDefaultTargets(caster: HeroViewComp, faction: number): Vec3[] { const targets: Vec3[] = []; const defaultX = faction === 0 ? 400 : -400; targets.push(v3(defaultX, 0, 0)); return targets; } /** * 查找附近的敌人 */ private findNearbyEnemies(caster: HeroViewComp, faction: number, range: number): Vec3[] { // 简化实现,实际应该查询ECS中的敌方实体 const enemies: Vec3[] = []; // 模拟敌人位置 const enemyX = faction === 0 ? 300 : -300; enemies.push(v3(enemyX, 0, 0)); enemies.push(v3(enemyX + 50, 20, 0)); return enemies; } /** * 查找血量低的友军 */ private findLowHealthAllies(caster: HeroViewComp, faction: number, range: number): Vec3[] { // 简化实现,实际应该查询ECS中的友方实体并按血量排序 const allies: Vec3[] = []; // 如果自己血量低,优先治疗自己 return allies; } }