refactor(技能系统): 重构施放系统以支持位置目标和实体ID目标

- 将技能目标从 HeroViewComp 数组改为位置向量和实体ID数组的组合
- 移除对 oops 框架和 GameEvent 的依赖,改为直接处理技能效果
- 新增 resolveFriendlyTargets 方法用于解析友方目标实体
- 新增 hasCastTarget 方法统一检查施放目标有效性
- 简化 applyPrimaryEffect 方法,分离伤害技能和增益技能的处理逻辑
This commit is contained in:
panw
2026-03-18 14:57:09 +08:00
parent ee16c228ec
commit cc1ca2f18b

View File

@@ -6,8 +6,6 @@ import { BuffsList, SkillConfig, SkillKind, SkillSet, TGroup } from "../common/c
import { Skill } from "../skill/Skill"; import { Skill } from "../skill/Skill";
import { smc } from "../common/SingletonModuleComp"; import { smc } from "../common/SingletonModuleComp";
import { GameConst } from "../common/config/GameConst"; import { GameConst } from "../common/config/GameConst";
import { oops } from "db://oops-framework/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { HType } from "../common/config/heroSet"; import { HType } from "../common/config/heroSet";
/** /**
@@ -25,7 +23,7 @@ import { HType } from "../common/config/heroSet";
@ecs.register('SCastSystem') @ecs.register('SCastSystem')
export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
debugMode: boolean = false; // 是否启用调试模式 debugMode: boolean = false; // 是否启用调试模式
private readonly emptyCastPlan = { skillId: 0, targets: [] as HeroViewComp[], isFriendly: false }; private readonly emptyCastPlan = { skillId: 0, isFriendly: false, targetPos: null as Vec3 | null, targetEids: [] as number[] };
private readonly meleeCastRange = 64; private readonly meleeCastRange = 64;
filter(): ecs.IMatcher { filter(): ecs.IMatcher {
@@ -41,11 +39,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
if (heroAttrs.is_dead || heroAttrs.is_reviving || heroAttrs.isStun() || heroAttrs.isFrost()) return; if (heroAttrs.is_dead || heroAttrs.is_reviving || heroAttrs.isStun() || heroAttrs.isFrost()) return;
heroAttrs.updateCD(this.dt); heroAttrs.updateCD(this.dt);
const castPlan = this.pickCastSkill(heroAttrs, heroView); const castPlan = this.pickCastSkill(heroAttrs, heroView);
if (castPlan.skillId === 0 || castPlan.targets.length === 0) return; if (!this.hasCastTarget(castPlan)) return;
this.castSkill(e, castPlan, heroAttrs, heroView); this.castSkill(castPlan, heroAttrs, heroView);
} }
private pickCastSkill(heroAttrs: HeroAttrsComp, heroView: HeroViewComp): { skillId: number; targets: HeroViewComp[]; isFriendly: boolean } { private pickCastSkill(heroAttrs: HeroAttrsComp, heroView: HeroViewComp): { skillId: number; isFriendly: boolean; targetPos: Vec3 | null; targetEids: number[] } {
const type = heroAttrs.type as HType; const type = heroAttrs.type as HType;
const maxRange = this.resolveMaxCastRange(heroAttrs, type); const maxRange = this.resolveMaxCastRange(heroAttrs, type);
const target = this.findNearestEnemyInRange(heroAttrs, heroView, maxRange); const target = this.findNearestEnemyInRange(heroAttrs, heroView, maxRange);
@@ -58,15 +56,17 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
if (isMainSkill && !heroAttrs.can_skill) continue; if (isMainSkill && !heroAttrs.can_skill) continue;
if (!isMainSkill && !heroAttrs.can_atk) continue; if (!isMainSkill && !heroAttrs.can_atk) continue;
if (this.isFriendlySkill(config.TGroup)) { if (this.isFriendlySkill(config.TGroup)) {
return { skillId: s_uuid, targets: [heroView], isFriendly: true }; const selfEid = heroView.ent?.eid;
if (typeof selfEid !== "number") continue;
return { skillId: s_uuid, isFriendly: true, targetPos: null, targetEids: [selfEid] };
} }
if (!target) continue; if (!target) continue;
return { skillId: s_uuid, targets: [target], isFriendly: false }; return { skillId: s_uuid, isFriendly: false, targetPos: target.node.position.clone(), targetEids: [] };
} }
return this.emptyCastPlan; return this.emptyCastPlan;
} }
private castSkill(entity: ecs.Entity, castPlan: { skillId: number; targets: HeroViewComp[]; isFriendly: boolean }, heroAttrs: HeroAttrsComp, heroView: HeroViewComp) { private castSkill(castPlan: { skillId: number; isFriendly: boolean; targetPos: Vec3 | null; targetEids: number[] }, heroAttrs: HeroAttrsComp, heroView: HeroViewComp) {
const s_uuid = castPlan.skillId; const s_uuid = castPlan.skillId;
const config = SkillSet[s_uuid]; const config = SkillSet[s_uuid];
if (!config) return; if (!config) return;
@@ -81,18 +81,13 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
heroView.scheduleOnce(() => { heroView.scheduleOnce(() => {
if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return; if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return;
if (castPlan.isFriendly) { if (castPlan.isFriendly) {
oops.message.dispatchEvent(GameEvent.CastHeroSkill, { const friendlyTargets = this.resolveFriendlyTargets(castPlan.targetEids, heroAttrs.fac);
casterEid: entity.eid, if (friendlyTargets.length === 0) return;
s_uuid, this.applyPrimaryEffect(s_uuid, config, heroView, friendlyTargets, null);
fac: heroAttrs.fac, this.applyExtraEffects(config, friendlyTargets);
targetEids: castPlan.targets.map(target => target.ent?.eid).filter((eid): eid is number => typeof eid === "number")
});
return; return;
} }
const validTargets = this.filterValidTargets(castPlan.targets); this.applyPrimaryEffect(s_uuid, config, heroView, [], castPlan.targetPos);
if (validTargets.length === 0) return;
this.applyPrimaryEffect(entity, s_uuid, config, heroView, validTargets);
this.applyExtraEffects(config, validTargets);
}, delay); }, delay);
if (isMainSkill) { if (isMainSkill) {
heroAttrs.triggerSkillCD(); heroAttrs.triggerSkillCD();
@@ -109,12 +104,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
skill.load(caster.node.position.clone(), parent, s_uuid, targetPos.clone(), caster, 0); skill.load(caster.node.position.clone(), parent, s_uuid, targetPos.clone(), caster, 0);
} }
private applyPrimaryEffect(casterEntity: ecs.Entity, s_uuid: number, config: SkillConfig, heroView: HeroViewComp, targets: HeroViewComp[]) { private applyPrimaryEffect(s_uuid: number, config: SkillConfig, heroView: HeroViewComp, targets: HeroViewComp[], targetPos: Vec3 | null) {
const kind = config.kind ?? SkillKind.Damage; const kind = config.kind ?? SkillKind.Damage;
if (kind === SkillKind.Damage) { if (kind === SkillKind.Damage) {
if (config.ap > 0) { if (config.ap <= 0 || !targetPos) return;
this.createSkillEntity(s_uuid, heroView, targets[0].node.position); this.createSkillEntity(s_uuid, heroView, targetPos);
}
return; return;
} }
for (const target of targets) { for (const target of targets) {
@@ -154,14 +148,25 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
} }
} }
private filterValidTargets(targets: HeroViewComp[]): HeroViewComp[] { private resolveFriendlyTargets(targetEids: number[], fac: number): HeroViewComp[] {
return targets.filter(target => { const targets: HeroViewComp[] = [];
if (!target || !target.node || !target.node.isValid) return false; for (const eid of targetEids) {
if (!target.ent) return false; const entity = ecs.getEntityByEid(eid);
const model = target.ent.get(HeroAttrsComp); if (!entity) continue;
if (!model || model.is_dead || model.is_reviving) return false; const model = entity.get(HeroAttrsComp);
return true; const view = entity.get(HeroViewComp);
}); if (!model || !view?.node) continue;
if (model.fac !== fac) continue;
if (model.is_dead || model.is_reviving) continue;
targets.push(view);
}
return targets;
}
private hasCastTarget(castPlan: { skillId: number; isFriendly: boolean; targetPos: Vec3 | null; targetEids: number[] }): boolean {
if (castPlan.skillId === 0) return false;
if (castPlan.isFriendly) return castPlan.targetEids.length > 0;
return !!castPlan.targetPos;
} }
private isFriendlySkill(group: TGroup): boolean { private isFriendlySkill(group: TGroup): boolean {