Files
heros/assets/script/game/skill/HeroSkillSystem.ts

235 lines
8.7 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 { Node, Vec3 } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroViewComp } from "../hero/HeroViewComp";
import { HeroSkillsComp } from "./heroSkillsComp";
import { SkillSet, TargetGroup, TargetType } from "../common/config/SkillSet";
import { CdType } from "../common/config/SkillSet";
import { DamageRequest, DamageType } from "../../game/damage/DamageComp";
/** 技能系统 */
@ecs.register('HeroSkillSystem')
export class HeroSkillSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
private updateInterval: number = 0.1; // 每0.1秒更新一次
private accumulator: number = 0;
filter(): ecs.IMatcher {
return ecs.allOf(HeroSkillsComp, HeroViewComp);
}
update(e: ecs.Entity) {
const view = e.get(HeroViewComp);
const skills = e.get(HeroSkillsComp);
// 使用固定时间步长更新
this.accumulator += this.dt;
while (this.accumulator >= this.updateInterval) {
this.accumulator -= this.updateInterval;
// 只在攻击状态触发技能
if (view.is_atking) {
this.processSkills(e, skills);
}
// 更新所有技能冷却
skills.skills.forEach(skillId => {
this.updateCooldown(skills, skillId);
});
}
}
/** 处理所有技能逻辑 */
private processSkills(entity: ecs.Entity, comp: HeroSkillsComp) {
comp.skills.forEach(skillId => {
const config = SkillSet[skillId];
if (!config) return;
// 检查释放条件
if (this.checkSkillCondition(entity, config)) {
this.castSkill(entity, skillId, config);
}
});
}
/** 更新技能冷却 */
private updateCooldown(comp: HeroSkillsComp, skillId: number) {
let cd = comp.cooldowns.has(skillId) ? comp.cooldowns.get(skillId)! : 0;
if (cd > 0) {
comp.cooldowns.set(skillId, cd - this.updateInterval);
}
}
/** 检查技能释放条件 */
private checkSkillCondition(entity: ecs.Entity, config: typeof SkillSet[keyof typeof SkillSet]): boolean {
const view = entity.get(HeroViewComp);
const comp = entity.get(HeroSkillsComp);
switch(config.CdType){
case CdType.SkillCD:
return (comp.cooldowns.get(config.uuid as number) ?? 0) <= 0;
case CdType.HeroCD:
return view.at >= view.cd;
case CdType.HeroPower:
return view.pw >= view.pwm;
}
}
/** 施放技能 */
private castSkill(caster: ecs.Entity, skillId: number, config: typeof SkillSet[keyof typeof SkillSet]) {
const view = caster.get(HeroViewComp);
const comp = caster.get(HeroSkillsComp);
console.log(view.hero_name+"施放技能",config.uuid,comp,view)
switch(config.CdType) {
case CdType.SkillCD:
view.as.max()
comp.cooldowns.set(skillId, config.cd); // 重置冷却时间
break;
case CdType.HeroCD:
view.as.atk()
view.at = view.at-view.cd; // 重置普攻计时器
break;
case CdType.HeroPower:
view.as.max()
view.pw = view.pw-view.pwm; // 重置能量计时器
break;
}
// 选择目标
const targets = this.selectTargets(caster, config);
// 应用技能效果
targets.forEach(target => {
this.applySkillEffect(caster, target, config);
});
// 重置计数器
if (config.count) {
comp.counters.set(skillId, (comp.counters.get(skillId) || 0) + 1);
}
// 触发技能动画等表现
}
/** 选择技能目标 */
private selectTargets(caster: ecs.Entity, config: typeof SkillSet[keyof typeof SkillSet]): ecs.Entity[] {
const casterView = caster.get(HeroViewComp);
const team = casterView.fac;
const isEnemyTeam = team === 0 ? 1 : 0;
// 第一阶段:基础目标筛选
let candidates = ecs.query(ecs.allOf(HeroViewComp)).filter(e => {
const view = e.get(HeroViewComp);
// 根据技能目标类型筛选
switch(config.TargetGroup) {
case TargetGroup.Enemy:
return view.fac !== team;
case TargetGroup.Ally:
return view.fac === team && e !== caster;
case TargetGroup.Self:
return e === caster;
default:
return true;
}
});
// 第二阶段:位置/血量等精细筛选
switch(config.TargetType) {
case TargetType.Frontline:
return this.filterFrontRow(candidates, isEnemyTeam);
case TargetType.Backline:
return this.filterBackRow(candidates, isEnemyTeam);
case TargetType.LowestHP:
return this.filterLowestHealth(candidates);
case TargetType.HighestHP:
return this.filterHighestHealth(candidates);
case TargetType.Melee:
return candidates.filter(e => e.get(HeroViewComp).type === 0);
case TargetType.Ranged:
return candidates.filter(e => e.get(HeroViewComp).type === 1);
case TargetType.SupportClass:
return candidates.filter(e => e.get(HeroViewComp).type === 2);
case TargetType.Random:
return this.pickRandomTarget(candidates, config.count || 1);
default:
return candidates;
}
}
/** 筛选最前排单位 */
private filterFrontRow(entities: ecs.Entity[], isEnemyTeam: number): ecs.Entity[] {
// 敌方最前排是x坐标最大的我方最前排是x坐标最小的
const keyPos = isEnemyTeam ?
Math.min(...entities.map(e => e.get(HeroViewComp).node.position.x)) :
Math.max(...entities.map(e => e.get(HeroViewComp).node.position.x));
return entities.filter(e =>
Math.abs(e.get(HeroViewComp).node.position.x - keyPos) < 10
);
}
/** 筛选最后排单位 */
private filterBackRow(entities: ecs.Entity[], isEnemyTeam: number): ecs.Entity[] {
// 敌方最后排是x坐标最小的我方最后排是x坐标最大的
const keyPos = isEnemyTeam ?
Math.max(...entities.map(e => e.get(HeroViewComp).node.position.x)) :
Math.min(...entities.map(e => e.get(HeroViewComp).node.position.x));
return entities.filter(e =>
Math.abs(e.get(HeroViewComp).node.position.x - keyPos) < 10
);
}
/** 筛选血量最低单位 */
private filterLowestHealth(entities: ecs.Entity[]): ecs.Entity[] {
const minHp = Math.min(...entities.map(e => e.get(HeroViewComp).hp));
return entities.filter(e => e.get(HeroViewComp).hp === minHp);
}
/** 筛选血量最高单位 */
private filterHighestHealth(entities: ecs.Entity[]): ecs.Entity[] {
const maxHp = Math.max(...entities.map(e => e.get(HeroViewComp).hp));
return entities.filter(e => e.get(HeroViewComp).hp === maxHp);
}
/** 随机选择目标 */
private pickRandomTarget(entities: ecs.Entity[], count: number): ecs.Entity[] {
const shuffled = [...entities].sort(() => 0.5 - Math.random());
return shuffled.slice(0, count);
}
/** 应用技能效果 */
private applySkillEffect(caster: ecs.Entity, target: ecs.Entity, config: typeof SkillSet[keyof typeof SkillSet]) {
// 创建伤害请求实体
const damageEntity = new ecs.Entity();
const req = damageEntity.add(DamageRequest);
req.source = caster;
req.target = target;
req.baseValue = caster.get(HeroViewComp).ap * (config.ap / 100);
req.delay = config.hited; // 添加命中延迟时间(秒)用于同步动画与伤害
// 播放技能特效
caster.get(HeroViewComp).playSkillEffect(config.uuid);
}
/** 应用负面状态 */
private applyDebuff(target: ecs.Entity, config: typeof SkillSet[keyof typeof SkillSet]) {
// 实现debuff逻辑...
}
/** 外部调用重置冷却 */
public resetSkillCooldown(entity: ecs.Entity, skillId: number) {
const comp = entity.get(HeroSkillsComp);
comp.resetCooldown(skillId);
}
/** 重置所有技能冷却 */
public resetAllCooldowns(entity: ecs.Entity) {
const comp = entity.get(HeroSkillsComp);
comp.resetAllCooldowns();
}
}