- 移除HeroViewComp中的调试日志 - 缩短技能释放前摇时间从0.3秒到0.1秒 - 重构Skill类,清理无用导入并优化属性传递 - 改进HeroAtkSystem,添加伤害数据深拷贝避免重复处理 - 完善SkillView,增加技能结束类型处理并优化伤害应用逻辑
277 lines
8.8 KiB
TypeScript
277 lines
8.8 KiB
TypeScript
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 = 0.1
|
||
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>(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;
|
||
}
|
||
|
||
|
||
} |