feat(battle): 重构技能施放与战斗距离系统
- 新增技能距离缓存机制,根据英雄类型动态计算最小和最大攻击范围 - 重构SCastSystem实现完整的技能施放逻辑,支持伤害、治疗、护盾和buff技能 - 在Hero和Monster初始化时调用updateSkillDistanceCache预计算技能距离 - 修改HeroMoveSystem和MonMoveSystem使用动态战斗范围,支持撤退逻辑 - 优化Skill实体创建,增加对象池支持 - 添加技能CD触发方法和状态检查方法
This commit is contained in:
@@ -1,13 +1,11 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { Vec3, v3 } from "cc";
|
||||
import { Vec3 } from "cc";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { HSSet, SkillSet, SType, TGroup, SkillConfig } from "../common/config/SkillSet";
|
||||
import { BuffsList, SkillConfig, SkillSet, SType, TGroup } from "../common/config/SkillSet";
|
||||
import { Skill } from "../skill/Skill";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { BoxSet, FacSet } from "../common/config/GameSet";
|
||||
import { GameConst } from "../common/config/GameConst";
|
||||
import { Attrs } from "../common/config/HeroAttrs";
|
||||
import { mLogger } from "../common/Logger";
|
||||
|
||||
/**
|
||||
@@ -36,7 +34,124 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
update(e: ecs.Entity): void {
|
||||
if(!smc.mission.play ) return;
|
||||
if(smc.mission.pause) return
|
||||
|
||||
const heroAttrs = e.get(HeroAttrsComp);
|
||||
const heroView = e.get(HeroViewComp);
|
||||
if (!heroAttrs || !heroView || !heroView.node) return;
|
||||
if (heroAttrs.is_dead || heroAttrs.is_reviving || heroAttrs.isStun() || heroAttrs.isFrost()) return;
|
||||
heroAttrs.updateCD(this.dt);
|
||||
if (!heroAttrs.is_atking) return;
|
||||
const castSkillId = this.pickCastSkill(heroAttrs, heroView);
|
||||
if (castSkillId === 0) return;
|
||||
this.castSkill(e, castSkillId, heroAttrs, heroView);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private pickCastSkill(heroAttrs: HeroAttrsComp, heroView: HeroViewComp): number {
|
||||
const skillCandidates = [heroAttrs.skill_id, heroAttrs.atk_id];
|
||||
for (const s_uuid of skillCandidates) {
|
||||
if (!s_uuid) continue;
|
||||
const config = SkillSet[s_uuid];
|
||||
if (!config) continue;
|
||||
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);
|
||||
if (targets.length === 0) continue;
|
||||
return s_uuid;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private castSkill(entity: ecs.Entity, s_uuid: number, heroAttrs: HeroAttrsComp, heroView: HeroViewComp) {
|
||||
const config = SkillSet[s_uuid];
|
||||
if (!config) return;
|
||||
const targets = this.findTargets(heroView, heroAttrs, config);
|
||||
if (targets.length === 0) return;
|
||||
heroView.playSkillEffect(s_uuid);
|
||||
const isMainSkill = s_uuid === heroAttrs.skill_id;
|
||||
const delay = GameConst.Battle.SKILL_CAST_DELAY;
|
||||
heroView.scheduleOnce(() => {
|
||||
if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return;
|
||||
if (config.SType === SType.damage) {
|
||||
this.createSkillEntity(s_uuid, heroView, targets[0].node.position);
|
||||
} else {
|
||||
this.applySupportSkill(entity, config, targets);
|
||||
}
|
||||
}, delay);
|
||||
if (isMainSkill) {
|
||||
heroAttrs.triggerSkillCD();
|
||||
} else {
|
||||
heroAttrs.triggerAtkCD();
|
||||
}
|
||||
}
|
||||
|
||||
private createSkillEntity(s_uuid: number, caster: HeroViewComp, targetPos: Vec3) {
|
||||
if (!caster.node || !caster.node.isValid) return;
|
||||
const parent = caster.node.parent;
|
||||
if (!parent) return;
|
||||
const skill = ecs.getEntity<Skill>(Skill);
|
||||
skill.load(caster.node.position.clone(), parent, s_uuid, targetPos.clone(), caster, 0);
|
||||
}
|
||||
|
||||
private applySupportSkill(casterEntity: ecs.Entity, config: SkillConfig, targets: HeroViewComp[]) {
|
||||
const casterAttrs = casterEntity.get(HeroAttrsComp);
|
||||
if (!casterAttrs) return;
|
||||
const ratio = Number(config.ap ?? 0) / 100;
|
||||
for (const target of targets) {
|
||||
if (!target.ent) continue;
|
||||
const model = target.ent.get(HeroAttrsComp);
|
||||
if (!model || model.is_dead) continue;
|
||||
if (config.SType === SType.heal) {
|
||||
const amount = model.hp_max * ratio;
|
||||
model.add_hp(amount, true);
|
||||
target.health(amount);
|
||||
continue;
|
||||
}
|
||||
if (config.SType === SType.shield) {
|
||||
const amount = model.hp_max * ratio;
|
||||
model.shield_max = Math.max(model.shield_max, amount);
|
||||
model.add_shield(amount, true);
|
||||
continue;
|
||||
}
|
||||
if (config.SType === SType.buff) {
|
||||
for (const buffId of config.buffs) {
|
||||
const buffConf = BuffsList[buffId];
|
||||
if (buffConf) {
|
||||
model.addBuff(buffConf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mLogger.log(this.debugMode, "SCastSystem", `[SCastSystem] ${casterAttrs.hero_name} 施放 ${config.name}`);
|
||||
}
|
||||
|
||||
private findTargets(caster: HeroViewComp, casterAttrs: HeroAttrsComp, config: SkillConfig): HeroViewComp[] {
|
||||
const range = Number(config.dis ?? 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];
|
||||
const currentPos = caster.node.position;
|
||||
const list: { view: HeroViewComp; dis: number; lane: number }[] = [];
|
||||
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, dis, lane });
|
||||
});
|
||||
list.sort((a, b) => {
|
||||
if (a.lane !== b.lane) return a.lane - b.lane;
|
||||
return a.dis - b.dis;
|
||||
});
|
||||
const maxTargets = Math.max(GameConst.Skill.MIN_TARGET_COUNT, config.t_num || 1);
|
||||
return list.slice(0, maxTargets).map(item => item.view);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user