fix(技能): 修正技能目标筛选和触发类型配置
- 移除未使用的反伤技能配置 (5000) - 交换技能 6001 和 6002 的 EType 配置 (animationEnd/collision),使空挥技能在动画结束时触发,电击技能在碰撞时触发 - 将绿箭 (6006) 和红箭 (6007) 的 EType 从 animationEnd 改为 collision,使其在碰撞时触发 - 重构 SCastSystem 的目标查找逻辑,将候选目标收集与筛选分离,提高性能并修复可能的目标查找错误
This commit is contained in:
@@ -26,6 +26,7 @@ import { GameConst } from "../common/config/GameConst";
|
||||
export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||
debugMode: boolean = false; // 是否启用调试模式
|
||||
private readonly emptyCastPlan = { skillId: 0, targets: [] as HeroViewComp[] };
|
||||
private readonly emptyCandidates: { view: HeroViewComp; attrs: HeroAttrsComp; dis: number; lane: number; isSameFac: boolean }[] = [];
|
||||
|
||||
filter(): ecs.IMatcher {
|
||||
return ecs.allOf(HeroAttrsComp, HeroViewComp);
|
||||
@@ -46,6 +47,8 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
}
|
||||
|
||||
private pickCastSkill(heroAttrs: HeroAttrsComp, heroView: HeroViewComp): { skillId: number; targets: HeroViewComp[] } {
|
||||
const range = heroAttrs.getCachedMaxSkillDistance() || GameConst.Battle.DEFAULT_SEARCH_RANGE;
|
||||
const candidates = this.collectCandidates(heroView, heroAttrs, range);
|
||||
const skillCandidates = [heroAttrs.skill_id, heroAttrs.atk_id];
|
||||
for (const s_uuid of skillCandidates) {
|
||||
if (!s_uuid) continue;
|
||||
@@ -54,7 +57,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
const isMainSkill = s_uuid === heroAttrs.skill_id;
|
||||
if (isMainSkill && !heroAttrs.can_skill) continue;
|
||||
if (!isMainSkill && !heroAttrs.can_atk) continue;
|
||||
const targets = this.findTargets(heroView, heroAttrs, config);
|
||||
const targets = this.findTargetsByCandidates(heroView, config, candidates);
|
||||
if (targets.length === 0) continue;
|
||||
return { skillId: s_uuid, targets };
|
||||
}
|
||||
@@ -150,57 +153,53 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
});
|
||||
}
|
||||
|
||||
private findTargets(caster: HeroViewComp, casterAttrs: HeroAttrsComp, config: SkillConfig): HeroViewComp[] {
|
||||
const range = casterAttrs.getCachedMaxSkillDistance() || GameConst.Battle.DEFAULT_SEARCH_RANGE;
|
||||
const isEnemy = config.TGroup === TGroup.Enemy;
|
||||
const isSelf = config.TGroup === TGroup.Self;
|
||||
const isTeam = config.TGroup === TGroup.Team || config.TGroup === TGroup.Ally;
|
||||
const isAll = config.TGroup === TGroup.All;
|
||||
if (isSelf) return [caster];
|
||||
private collectCandidates(caster: HeroViewComp, casterAttrs: HeroAttrsComp, range: number): { view: HeroViewComp; attrs: HeroAttrsComp; dis: number; lane: number; isSameFac: boolean }[] {
|
||||
if (!caster || !caster.node || !caster.node.isValid) return this.emptyCandidates;
|
||||
const currentPos = caster.node.position;
|
||||
const list: { view: HeroViewComp; attrs: HeroAttrsComp; dis: number; lane: number }[] = [];
|
||||
const list: { view: HeroViewComp; attrs: HeroAttrsComp; dis: number; lane: number; isSameFac: boolean }[] = [];
|
||||
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).forEach(ent => {
|
||||
const targetAttrs = ent.get(HeroAttrsComp);
|
||||
const targetView = ent.get(HeroViewComp);
|
||||
if (!targetAttrs || !targetView || !targetView.node || targetAttrs.is_dead) return;
|
||||
if (targetView === caster) return;
|
||||
const isSameFac = targetAttrs.fac === casterAttrs.fac;
|
||||
if (isEnemy && isSameFac) return;
|
||||
if (isTeam && !isSameFac) return;
|
||||
if (!isEnemy && !isTeam && !isAll) return;
|
||||
const dis = Math.abs(currentPos.x - targetView.node.position.x);
|
||||
if (dis > range) return;
|
||||
const lane = Math.abs(currentPos.y - targetView.node.position.y);
|
||||
list.push({ view: targetView, attrs: targetAttrs, dis, lane });
|
||||
const isSameFac = targetAttrs.fac === casterAttrs.fac;
|
||||
list.push({ view: targetView, attrs: targetAttrs, dis, lane, isSameFac });
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
private findTargetsByCandidates(caster: HeroViewComp, config: SkillConfig, candidates: { view: HeroViewComp; attrs: HeroAttrsComp; dis: number; lane: number; isSameFac: boolean }[]): HeroViewComp[] {
|
||||
const isEnemy = config.TGroup === TGroup.Enemy;
|
||||
const isSelf = config.TGroup === TGroup.Self;
|
||||
const isTeam = config.TGroup === TGroup.Team || config.TGroup === TGroup.Ally;
|
||||
const isAll = config.TGroup === TGroup.All;
|
||||
if (isSelf) return [caster];
|
||||
if (!isEnemy && !isTeam && !isAll) return this.emptyCastPlan.targets;
|
||||
const list = candidates.filter(item => {
|
||||
if (isEnemy && item.isSameFac) return false;
|
||||
if (isTeam && !item.isSameFac) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
list.sort((a, b) => {
|
||||
// 优先检查是否在同一行 (除了特殊目标类型)
|
||||
// 如果是寻找特殊目标(如最低血量),通常忽略行优先,但在范围内全搜索
|
||||
// 但如果设计要求"最近的优先",则通常还是先看行。
|
||||
// 这里假设 TType 优先级高于 Lane 优先级,或者在 TType 相同情况下比较 Lane
|
||||
|
||||
const type = config.TType ?? TType.Frontline;
|
||||
switch (type) {
|
||||
case TType.Backline:
|
||||
// 后排:距离最远优先
|
||||
if (a.lane !== b.lane) return a.lane - b.lane; // 先同行
|
||||
if (a.lane !== b.lane) return a.lane - b.lane;
|
||||
return b.dis - a.dis;
|
||||
case TType.LowestHP:
|
||||
// 最低血量
|
||||
if (a.attrs.hp !== b.attrs.hp) return a.attrs.hp - b.attrs.hp;
|
||||
return a.dis - b.dis; // 血量相同选最近
|
||||
return a.dis - b.dis;
|
||||
case TType.HighestHP:
|
||||
// 最高血量
|
||||
if (a.attrs.hp !== b.attrs.hp) return b.attrs.hp - a.attrs.hp;
|
||||
return a.dis - b.dis;
|
||||
case TType.HighestAP:
|
||||
// 最高攻击
|
||||
if (a.attrs.ap !== b.attrs.ap) return b.attrs.ap - a.attrs.ap;
|
||||
return a.dis - b.dis;
|
||||
case TType.Frontline:
|
||||
default:
|
||||
// 前排:距离最近优先 (默认)
|
||||
if (a.lane !== b.lane) return a.lane - b.lane;
|
||||
return a.dis - b.dis;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user