重构了 技能系统,还需要完善

This commit is contained in:
panw
2025-10-30 15:12:49 +08:00
parent 1281cbd32d
commit 55646c3a11
27 changed files with 1022 additions and 595 deletions

View File

@@ -0,0 +1,270 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { Vec3, v3 } from "cc";
import { HeroSkillsComp } from "./HeroSkills";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { SkillSet, SType } from "../common/config/SkillSet";
import { SkillEnt } from "../skill/SkillEnt";
import { smc } from "../common/SingletonModuleComp";
/**
* ==================== 施法请求标记组件 ====================
*
* 用途:
* - 标记角色想要施放某个技能
* - 由外部如AI系统、玩家输入添加
* - 施法系统处理后自动移除
*/
@ecs.register('CastSkillRequest')
export class CastSkillRequestComp extends ecs.Comp {
/** 技能索引(在 HeroSkillsComp.skills 中的位置) */
skillIndex: number = 0;
/** 目标位置数组(由请求者提供) */
targetPositions: Vec3[] = [];
reset() {
this.skillIndex = 0;
this.targetPositions = [];
}
}
/**
* ==================== 技能施法系统 ====================
*
* 职责:
* 1. 监听 CastSkillRequestComp 标记组件
* 2. 检查施法条件CD、MP、状态
* 3. 扣除资源MP
* 4. 创建技能实体
* 5. 触发施法动画
* 6. 移除请求标记
*
* 设计理念:
* - 使用标记组件驱动,符合 ECS 理念
* - 施法检查与执行分离
* - 自动处理资源消耗和CD重置
*/
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, // 施法者
0 // 额外伤害暂时为0
);
}
}
/**
* ==================== 技能CD更新系统 ====================
*
* 职责:
* 1. 每帧更新所有角色的技能CD
* 2. 自动递减CD时间
*
* 设计理念:
* - 独立的CD管理系统
* - 只负责时间递减,不处理施法逻辑
*/
export class SkillCDSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(HeroSkillsComp);
}
update(e: ecs.Entity): void {
const skillsData = e.get(HeroSkillsComp);
if (!skillsData) return;
// 更新所有技能CD
skillsData.updateCDs(this.dt);
}
}
/**
* ==================== 自动施法系统 ====================
*
* 职责:
* 1. 检测可施放的技能
* 2. 根据策略自动施法AI
* 3. 选择目标
* 4. 添加施法请求标记
*
* 设计理念:
* - 负责"何时施法"的决策
* - 通过添加 CastSkillRequestComp 触发施法
* - 可被玩家输入系统或AI系统复用
*/
export class SkillAutocastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(HeroSkillsComp, HeroAttrsComp, HeroViewComp);
}
update(e: ecs.Entity): void {
const skillsData = e.get(HeroSkillsComp);
const heroModel = e.get(HeroAttrsComp);
const heroView = e.get(HeroViewComp);
if (!skillsData || !heroModel || !heroView) return;
// 检查基本条件
if (heroModel.is_dead || heroModel.isStun() || heroModel.isFrost()) return;
// 检查是否正在攻击(只有攻击时才释放技能)
if (!heroView.is_atking) return;
// 获取所有可施放的技能
const readySkills = skillsData.getReadySkills(heroModel.mp);
if (readySkills.length === 0) return;
// 选择第一个可施放的伤害技能
for (const skillIndex of readySkills) {
const skill = skillsData.getSkill(skillIndex);
if (!skill) continue;
const config = SkillSet[skill.uuid];
if (!config || config.SType !== SType.damage) continue;
// ✅ 添加施法请求标记组件
const request = e.add(CastSkillRequestComp) as CastSkillRequestComp;
request.skillIndex = skillIndex;
request.targetPositions = this.selectTargets(heroView);
// 一次只施放一个技能
break;
}
}
/**
* 选择目标位置
*/
private selectTargets(caster: HeroViewComp): Vec3[] {
// 简化版:选择最前方的敌人
const targets: Vec3[] = [];
// 这里可以调用 SkillConComp 的目标选择逻辑
// 暂时返回默认位置
const heroModel = caster.ent.get(HeroAttrsComp);
const fac = heroModel?.fac ?? 0;
const defaultX = fac === 0 ? 400 : -400;
targets.push(v3(defaultX, 0, 0));
return targets;
}
}