Files
heros/assets/script/game/hero/SCastSystem.ts
walkpan 2f19433a0a feat(skill): 重构技能系统,新增技能数据组件和移动组件
refactor(skill): 移除旧技能组件和文档,优化技能配置结构

fix(skill): 修正技能预制体配置错误,统一技能运行类型字段

docs(skill): 删除过时的技能系统说明文档

perf(skill): 优化技能加载逻辑,减少资源消耗

style(skill): 调整代码格式,提高可读性
2025-10-31 00:35:51 +08:00

149 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { Vec3, v3 } from "cc";
import { CastSkillRequestComp } from "./STagComps";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { SkillSet, SType } from "../common/config/SkillSet";
import { SkillEnt } from "../skill/SkillEnt";
import { HeroSkillsComp } from "./HeroSkills";
/**
* ==================== 技能施法系统 ====================
*
* 职责:
* 1. 监听 CastSkillRequestComp 标记组件
* 2. 检查施法条件CD、MP、状态
* 3. 扣除资源MP
* 4. 创建技能实体
* 5. 触发施法动画
* 6. 移除请求标记
*
* 设计理念:
* - 使用标记组件驱动,符合 ECS 理念
* - 施法检查与执行分离
* - 自动处理资源消耗和CD重置
*/
@ecs.register('SkillCastSystem')
export class SkillCastSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem {
/**
* 过滤器:拥有技能数据 + 施法请求的实体
*/
filter(): ecs.IMatcher {
return ecs.allOf(HeroSkillsComp, HeroAttrsComp, CastSkillRequestComp);
}
/**
* 实体进入时触发(即请求施法时)
*/
entityEnter(e: ecs.Entity): void {
const skillsData = e.get(HeroSkillsComp);
const heroModel = e.get(HeroAttrsComp);
const request = e.get(CastSkillRequestComp);
const heroView = e.get(HeroViewComp);
// 1. 验证数据完整性
if (!skillsData || !heroModel || !request || !heroView) {
console.warn("[SkillCastSystem] 数据不完整,取消施法");
e.remove(CastSkillRequestComp);
return;
}
// 2. 获取技能数据
const skill = skillsData.getSkill(request.skillIndex);
if (!skill) {
console.warn(`[SkillCastSystem] 技能索引无效: ${request.skillIndex}`);
e.remove(CastSkillRequestComp);
return;
}
// 3. 检查施法条件
if (!this.checkCastConditions(skillsData, heroModel, request.skillIndex)) {
e.remove(CastSkillRequestComp);
return;
}
// 4. 执行施法
this.executeCast(e, skill, request.targetPositions, heroView);
// 5. 扣除资源和重置CD
heroModel.mp -= skill.cost;
skillsData.resetCD(request.skillIndex);
// 6. 移除请求标记
e.remove(CastSkillRequestComp);
}
/**
* 检查施法条件
*/
private checkCastConditions(skillsData: HeroSkillsComp, heroModel: HeroAttrsComp, skillIndex: number): boolean {
// 检查角色状态
if (heroModel.is_dead) {
return false;
}
// 检查控制状态(眩晕、冰冻)
if (heroModel.isStun() || heroModel.isFrost()) {
return false;
}
// 检查CD和MP
if (!skillsData.canCast(skillIndex, heroModel.mp)) {
return false;
}
return true;
}
/**
* 执行施法
*/
private executeCast(casterEntity: ecs.Entity, skill: any, targetPositions: Vec3[], heroView: HeroViewComp) {
const config = SkillSet[skill.uuid];
if (!config) {
console.error("[SkillCastSystem] 技能配置不存在:", skill.uuid);
return;
}
// 1. 播放施法动画
heroView.playSkillEffect(skill.uuid);
// 2. 延迟创建技能实体(等待动画)
const delay = config.with ?? 0.3; // 施法前摇时间
heroView.scheduleOnce(() => {
this.createSkillEntity(skill.uuid, heroView, targetPositions);
}, delay);
const heroModel = casterEntity.get(HeroAttrsComp);
console.log(`[SkillCastSystem] ${heroModel?.hero_name ?? '未知'} 施放技能: ${config.name}`);
}
/**
* 创建技能实体
*/
private createSkillEntity(skillId: number, caster: HeroViewComp, targetPositions: Vec3[]) {
// 检查节点有效性
if (!caster.node || !caster.node.isValid) {
console.warn("[SkillCastSystem] 施法者节点无效");
return;
}
// 获取场景节点
const parent = caster.node.parent;
if (!parent) {
console.warn("[SkillCastSystem] 场景节点无效");
return;
}
// ✅ 使用现有的 SkillEnt 创建技能
const skillEnt = ecs.getEntity<SkillEnt>(SkillEnt);
skillEnt.load(
caster.node.position, // 起始位置
parent, // 父节点
skillId, // 技能ID
targetPositions, // 目标位置数组
caster, // 施法者
);
}
}