refactor: 重构技能系统并移除自动施法模块

- 删除 SACastSystem 及其 meta 文件,移除自动施法逻辑
- 重构 HeroAttrsComp 中的 Buff 处理逻辑,修复百分比计算问题
  - 将治疗和护盾 Buff 的 BType 从 VALUE 改为 RATIO
  - 添加 resolveBuffValue 方法正确计算基于最大生命值的百分比值
  - 修复 applyAttrChange 中 RATIO 类型的叠加逻辑
- 添加 HeroBuffSystem 系统,将 Buff 更新逻辑从 HeroAttrsComp 中分离
- 优化 SkillView 的销毁逻辑,避免直接调用 destroy 方法
  - 禁用碰撞体并设置节点为 inactive 状态
This commit is contained in:
panw
2026-03-13 09:52:16 +08:00
parent d626a6e5c2
commit 6170f47ca6
5 changed files with 47 additions and 686 deletions

View File

@@ -314,9 +314,9 @@ export const BuffsList: Record<number, BuffConf> = {
// ========== 治疗与护盾 (转换自原 SType) ========== 10300 - 10399
// 治疗 (基于攻击力百分比)
10301: { uuid: 10301, name: "治疗", icon: "1292", buff: Attrs.hp, BType: BType.VALUE, value: 30, time: 0, chance: 1, info: "回复30%生命值" },
10301: { uuid: 10301, name: "治疗", icon: "1292", buff: Attrs.hp, BType: BType.RATIO, value: 30, time: 0, chance: 1, info: "回复30%最大生命值" },
// 护盾 (基于攻击力百分比)
10302: { uuid: 10302, name: "护盾", icon: "1255", buff: Attrs.shield, BType: BType.VALUE, value: 30, time: 0, chance: 1, info: "获得30%护盾" },
10302: { uuid: 10302, name: "护盾", icon: "1255", buff: Attrs.shield, BType: BType.RATIO, value: 30, time: 0, chance: 1, info: "获得30%最大生命值护盾" },
// ========== 减益类 Buff (属性降低) ========== 10200 - 10299
// 减速 (移动速度降低)

View File

@@ -5,6 +5,7 @@ import { Attrs, BType } from "../common/config/HeroAttrs";
import { BuffConf, SkillDisVal, SkillRange } from "../common/config/SkillSet";
import { HeroInfo, HType } from "../common/config/heroSet";
import { mLogger } from "../common/Logger";
import { smc } from "../common/SingletonModuleComp";
import { _decorator } from "cc";
const { property } = _decorator;
@@ -152,39 +153,45 @@ export class HeroAttrsComp extends ecs.Comp {
* @param buffConf buff 配置
*/
addBuff(buffConf: BuffConf) {
// 1. 如果是永久性增益time=0直接修改基础属性
const applyType = buffConf.BType === BType.BOOLEAN ? BType.BOOLEAN : BType.VALUE;
const resolvedValue = applyType === BType.BOOLEAN
? buffConf.value
: this.resolveBuffValue(buffConf.buff, buffConf.value, buffConf.BType);
if (buffConf.time <= 0) {
this.applyAttrChange(buffConf.buff, buffConf.value, buffConf.BType);
this.applyAttrChange(buffConf.buff, resolvedValue, applyType);
return;
}
// 2. 临时性 Buff/Debuff 处理
// 区分存储列表
const targetList = buffConf.isDebuff ? this.DEBUFFS : this.BUFFS;
// 确保列表初始化
// 注意:这里我们用 buffConf.buff (属性名) 作为 key而不是 buffConf.uuid
// 这样同一种属性的 buff 可以叠加或覆盖
const attrKey = buffConf.buff as unknown as number; // 强制转换 key 类型以适配 Record
if (!targetList[attrKey]) {
targetList[attrKey] = [];
}
const currentBuffs = targetList[attrKey];
// 查找是否已有同源的 buff (这里假设 uuid 相同为同源,或者简单点直接追加)
// 策略直接追加update 时统一计算
// 记录添加时的数值,方便后续移除(虽然 update 是重新计算总值的)
currentBuffs.push({
value: buffConf.value,
BType: buffConf.BType,
value: resolvedValue,
BType: applyType,
time: buffConf.time
});
// 立即应用一次属性变更(增量)
this.applyAttrChange(buffConf.buff, buffConf.value, buffConf.BType);
this.applyAttrChange(buffConf.buff, resolvedValue, applyType);
mLogger.log(this.debugMode, 'HeroAttrs', `添加Buff: ${buffConf.name}, 属性:${buffConf.buff}, 值:${buffConf.value}, 时间:${buffConf.time}`);
mLogger.log(this.debugMode, 'HeroAttrs', `添加Buff: ${buffConf.name}, 属性:${buffConf.buff}, 值:${resolvedValue}, 时间:${buffConf.time}`);
}
private resolveBuffValue(attr: Attrs, value: number, type: BType): number {
if (type !== BType.RATIO) return value;
if (attr === Attrs.hp || attr === Attrs.shield) {
return this.hp_max * value / 100;
}
if (attr === Attrs.hp_max || attr === Attrs.shield_max) {
return this[attr] * value / 100;
}
if (typeof this[attr] === "number") {
return (this[attr] as number) * value / 100;
}
return value;
}
/**
@@ -212,43 +219,10 @@ export class HeroAttrsComp extends ecs.Comp {
return;
}
// 处理百分比
// 注意:百分比通常是基于基础值计算,这里简化为直接修改当前值
// 如果需要严格的基础值+百分比,需要维护 baseAttrs 和 bonusAttrs
// 当前架构直接修改属性,百分比暂时按 (当前值 * 百分比) 或 (基础值 * 百分比) 处理
// 鉴于属性系统中 hp/hp_max 等已经是数值,这里百分比暂定为:属性 = 属性 + (属性 * 百分比)
// 但对于 addBuff 这种临时增益,百分比通常是基于"基础值"的。
// 由于没有显式的 base_ap这里简化处理
// VALUE: 直接加减
// RATIO: 简单处理为 value 就是最终加成值(需要配置表里填好的数值),或者基于当前值
// 通常配置表填 10 表示 10%,即 0.1。
// 但这里的 value 已经是 number。假设配置 10 就是 10 点,或者 10%。
// 修正:根据 BType 处理
if (type === BType.RATIO) {
// 假设 value 是百分比整数,如 10 代表 10%
// 必须基于某个基数。这里暂时基于当前值(不严谨,但在没有 base 属性的情况下只能这样,或者基于 100?
// 更合理的做法:如果是 RATIOvalue 应该在配置时就转为实际数值,或者这里基于当前属性计算
// 考虑到 Attrs 定义里有很多非数值属性(如状态),需要特殊处理
// 针对状态类属性IN_FROST 等),通常是 BOOLEAN不走 RATIO
// 针对数值类ap, hpRATIO 应该是 value% * current
// 简化:目前只支持 VALUE 型直接加减。RATIO 型需要更复杂的属性系统支持Base + Add + Mul
// 这里的实现先按 VALUE 处理,如果以后需要 RATIO建议在 addBuff 前计算好 value
// 或者:约定 RATIO 时value 是基于当前值的百分比增量
const ratio = finalValue / 100;
// 注意:这里直接修改 this[attr] 会导致多次叠加问题。
// 临时 Buff 的正确做法是Update 中每一帧重置属性 -> 应用所有 Buff。
// 但当前架构似乎是“增量修改”式的。
// “增量修改”式在移除时很麻烦(尤其是百分比)。
// 妥协方案:只支持 VALUE 加减。配置表里的 RATIO 需要在逻辑层转为 VALUE。
// 但为了兼容 BType.RATIO这里暂时按 (当前值 * ratio) 计算增量
// 这会导致 recursive 问题,所以严谨的项目里属性必须分层。
// 鉴于“少可用原则”,这里仅处理 VALUE 和 BOOLEAN
finalValue = this.resolveBuffValue(attr, finalValue, BType.RATIO);
if (typeof this[attr] === 'number') {
// 警告:直接改写数值可能不准确
this[attr] = (this[attr] as number) * (1 + ratio);
this[attr] = (this[attr] as number) + finalValue;
}
} else if (type === BType.BOOLEAN) {
// 布尔型/状态型value > 0 为 true/计数+1移除时 -1
@@ -475,5 +449,19 @@ export class HeroAttrsComp extends ecs.Comp {
}
@ecs.register('HeroBuffSystem')
export class HeroBuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(HeroAttrsComp);
}
update(e: ecs.Entity): void {
if (!smc.mission.play || smc.mission.pause) return;
const attrs = e.get(HeroAttrsComp);
if (!attrs || attrs.is_dead) return;
attrs.updateBuffsDebuffs(this.dt);
}
}

View File

@@ -1,621 +0,0 @@
// import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
// import { Vec3, v3 } from "cc";
// import { HeroAttrsComp } from "./HeroAttrsComp";
// import { HeroViewComp } from "./HeroViewComp";
// import { HSSet, SkillSet, SType, TGroup, SkillConfig } from "../common/config/SkillSet";
// import { HeroSkillsComp, SkillSlot } from "./HeroSkills";
// import { Skill } from "../skill/Skill";
// import { smc } from "../common/SingletonModuleComp";
// import { TalComp } from "./TalComp";
// import { TalEffet, TriType } from "../common/config/TalSet";
// import { BoxSet, FacSet } from "../common/config/GameSet";
// import { GameConst } from "../common/config/GameConst";
// import { Attrs } from "../common/config/HeroAttrs";
// import { mLogger } from "../common/Logger";
// /**
// * ==================== 自动施法系统 ====================
// *
// * 职责:
// * 1. 检测可施放的技能
// * 2. 根据策略自动施法AI
// * 3. 选择目标
// * 4. 添加施法请求标记
// *
// * 设计理念:
// * - 负责"何时施法"的决策
// * - 通过添加 CSRequestComp 触发施法
// * - 可被玩家输入系统或AI系统复用
// * - 支持多种AI策略
// */
// @ecs.register('SACastSystem')
// export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
// debugMode: boolean = false; // 是否启用调试模式
// filter(): ecs.IMatcher {
// return ecs.allOf(HeroSkillsComp, HeroAttrsComp, HeroViewComp);
// }
// update(e: ecs.Entity): void {
// if(!smc.mission.play ) return;
// if(smc.mission.pause) return
// const skills = e.get(HeroSkillsComp);
// if (!skills) return;
// // AI 降频每0.2秒执行一次
// skills.ai_timer += this.dt;
// if (skills.ai_timer < GameConst.Battle.AI_CHECK_INTERVAL) return;
// skills.ai_timer = 0;
// const heroAttrs = e.get(HeroAttrsComp);
// const heroView = e.get(HeroViewComp);
// if (!heroAttrs || !heroView) return;
// // 检查基本条件
// if (heroAttrs.is_dead || heroAttrs.is_reviving || heroAttrs.isStun() || heroAttrs.isFrost()) return;
// // 移除 is_atking 检查实现只要距离和CD满足即施法
// // if (!heroAttrs.is_atking) return;
// const readySkills = skills.getReadySkills();
// if (readySkills.length === 0) return;
// // 选择第一个可施放的技能(支持伤害/治疗/护盾)
// for (const s_uuid of readySkills) {
// const skill = skills.getSkill(s_uuid);
// if (!skill) continue;
// if (skill.hset === HSSet.max && !skills.max_auto) continue;
// const config = SkillSet[skill.s_uuid];
// if (!config) continue;
// // 根据技能类型检查目标
// if (config.SType === SType.damage) {
// if (!this.hasEnemyInSkillRange(heroView, heroAttrs, skill.dis)) continue;
// } else if (config.SType === SType.heal || config.SType === SType.shield) {
// if (!this.hasTeamInSkillRange(heroView, heroAttrs, skill.dis)) continue;
// } else if (config.SType === SType.buff) {
// if (!this.hasBuffTarget(heroView, heroAttrs, skill.dis, config.TGroup)) continue;
// }
// // ✅ 开始执行施法
// this.startCast(e, skill, skill.hset);
// // 一次只施放一个技能
// break;
// }
// }
// private startCast(e: ecs.Entity,skill:SkillSlot,hset:HSSet): boolean {
// if (!skill||!e) return false
// const skills = e.get(HeroSkillsComp);
// const heroAttrs = e.get(HeroAttrsComp);
// const heroView = e.get(HeroViewComp);
// // 3. 检查施法条件
// if (!this.checkCastConditions(skills, heroAttrs, skill.s_uuid)) return false
// // 4. 执行施法
// const castSucess = this.executeCast(e, skill.s_uuid, heroView,hset);
// // 5. 扣除资源和重置CD
// if (castSucess) {
// // 🔥 怪物不消耗蓝
// if (heroAttrs.fac !== FacSet.MON) {
// // 手动更新技能距离缓存
// heroAttrs.updateSkillDistanceCache(skills);
// }
// skills.resetCD(skill.s_uuid);
// }
// return castSucess;
// }
// public manualCast(e: ecs.Entity, s_uuid: number): boolean {
// if (!e) return false
// const skills = e.get(HeroSkillsComp)
// const heroAttrs = e.get(HeroAttrsComp)
// const heroView = e.get(HeroViewComp)
// if (!skills || !heroAttrs || !heroView) return false
// const slot = skills.getSkill(s_uuid)
// if (!slot) return false
// return this.startCast(e, slot, slot.hset)
// }
// public manualCastMax(e: ecs.Entity): boolean {
// const skills = e.get(HeroSkillsComp)
// if (!skills) return false
// for (const key in skills.skills) {
// const s_uuid = Number(key)
// const slot = skills.getSkill(s_uuid)
// if (slot && slot.hset === HSSet.max) {
// return this.manualCast(e, s_uuid)
// }
// }
// return false
// }
// /**
// * 检查施法条件
// */
// private checkCastConditions(skills: HeroSkillsComp, heroAttrs: HeroAttrsComp, s_uuid: number): boolean {
// // 检查角色状态
// if (heroAttrs.is_dead) {
// return false;
// }
// // 检查控制状态(眩晕、冰冻)
// if (heroAttrs.isStun() || heroAttrs.isFrost()) {
// return false;
// }
// // 检查CD
// if (!skills.canCast(s_uuid)) {
// return false;
// }
// return true;
// }
// /**
// * 执行施法
// */
// private executeCast(casterEntity: ecs.Entity, s_uuid: number, heroView: HeroViewComp,hset:HSSet): boolean {
// const heroAttrs=casterEntity.get(HeroAttrsComp)
// const config = SkillSet[s_uuid];
// if (!config) {
// mLogger.error(this.debugMode, 'SACastSystem', "[SACastSystem] 技能配置不存在:", s_uuid);
// return false;
// }
// // 1. 播放施法动画
// heroView.playSkillEffect(s_uuid);
// /**********************天赋处理*************************************************************************/
// // 2. 更新攻击类型的天赋触发值,技能和必杀级
// if(casterEntity.has(TalComp)){
// const talComp = casterEntity.get(TalComp);
// if (hset === HSSet.atk) talComp.updateCur(TriType.ATK);
// if (hset === HSSet.skill) talComp.updateCur(TriType.SKILL);
// }
// /**********************天赋处理*************************************************************************/
// // 根据技能类型执行不同逻辑
// if (config.SType === SType.heal) {
// return this.executeHealSkill(casterEntity, s_uuid, heroView, hset);
// } else if (config.SType === SType.shield) {
// return this.executeShieldSkill(casterEntity, s_uuid, heroView, hset);
// } else if (config.SType === SType.buff) {
// return this.executeBuffSkill(casterEntity, s_uuid, heroView, hset);
// }
// // 获取目标位置(伤害技能)
// let targets = this.sTargets(heroView, s_uuid);
// if (targets.length === 0) {
// mLogger.warn(this.debugMode, 'SACastSystem', "[SACastSystem] 没有找到有效目标");
// return false;
// }
// // 2.1 普通攻击逻辑
// if (hset === HSSet.atk){
// let delay = GameConst.Battle.SKILL_CAST_DELAY
// let ext_dmg = heroAttrs.useCountValTal(TalEffet.ATK_DMG);
// heroView.scheduleOnce(() => {
// this.createSkill(s_uuid, heroView,targets,ext_dmg);
// }, delay);
// //风怒wfuny 只针对 普通攻击起效
// if (heroAttrs.useCountTal(TalEffet.WFUNY)){
// let ext2_dmg = heroAttrs.useCountValTal(TalEffet.ATK_DMG);
// let delay = GameConst.Battle.SKILL_CAST_DELAY
// heroView.playSkillEffect(s_uuid);
// //需要再添加 风怒动画
// heroView.scheduleOnce(() => {
// this.createSkill(s_uuid, heroView,targets,ext2_dmg);
// },delay);
// }
// }
// // 2.2 技能攻击逻辑
// if(hset === HSSet.skill){
// let delay = GameConst.Battle.SKILL_CAST_DELAY
// let ext_dmg = heroAttrs.useCountValTal(TalEffet.SKILL_DMG);
// heroView.scheduleOnce(() => {
// this.createSkill(s_uuid, heroView,targets,ext_dmg);
// }, delay);
// // 双技能 只针对 技能起效
// if(heroAttrs.useCountTal(TalEffet.D_SKILL)){
// let ext2_dmg = heroAttrs.useCountValTal(TalEffet.SKILL_DMG);
// let delay = GameConst.Battle.SKILL_CAST_DELAY
// heroView.playSkillEffect(s_uuid);
// //需要再添加 双技能动画
// heroView.scheduleOnce(() => {
// this.createSkill(s_uuid, heroView,targets,ext2_dmg);
// },delay);
// }
// }
// // 2.3 必杀技能逻辑
// if(hset === HSSet.max){
// let delay = GameConst.Battle.SKILL_CAST_DELAY
// heroView.playSkillEffect(s_uuid);
// //需要再添加 最大伤害动画
// heroView.scheduleOnce(() => {
// this.createSkill(s_uuid, heroView,targets);
// },delay);
// }
// return true;
// }
// /**
// * 创建技能实体
// */
// private createSkill(s_uuid: number, caster: HeroViewComp,targets:Vec3[]=[],ext_dmg:number=0) {
// // 检查节点有效性
// if (!caster.node || !caster.node.isValid) {
// mLogger.warn(this.debugMode, 'SACastSystem', "[SACastSystem] 施法者节点无效");
// return;
// }
// // 获取场景节点
// const parent = caster.node.parent;
// if (!parent) {
// mLogger.warn(this.debugMode, 'SACastSystem', "[SACastSystem] 场景节点无效");
// return;
// }
// // 创建技能实体
// const skill = ecs.getEntity<Skill>(Skill);
// // 获取施法者位置作为起始位置
// const startPos = caster.node.position.clone();
// const targetPos = targets[0]; // 使用第一个目标位置
// // mLogger.log(this.debugMode, 'SACastSystem', `[SACastSystem]: ${s_uuid}, 起始位置: ${startPos}, 目标位置: ${targetPos}`);
// // 加载技能实体(包括预制体、组件初始化等)
// skill.load(startPos, parent, s_uuid, targetPos, caster,ext_dmg);
// }
// /**
// * 选择目标位置
// */
// private sTargets(caster: HeroViewComp, s_uuid: number): Vec3[] {
// const heroAttrs = caster.ent.get(HeroAttrsComp);
// if (!heroAttrs) return [];
// const config = SkillSet[s_uuid];
// if (!config) return this.sDefaultTargets(caster, heroAttrs.fac);
// const maxTargets = Math.max(GameConst.Skill.MIN_TARGET_COUNT, config.t_num ?? 1);
// const targets = this.sDamageTargets(caster, config, maxTargets);
// if (targets.length === 0) {
// targets.push(...this.sDefaultTargets(caster, heroAttrs.fac));
// }
// return targets;
// }
// /**
// * 选择伤害技能目标
// */
// private sDamageTargets(caster: HeroViewComp, config: SkillConfig, maxTargets: number): Vec3[] {
// const targets: Vec3[] = [];
// const heroAttrs = caster.ent.get(HeroAttrsComp);
// if (!heroAttrs) return targets;
// const range = Number(config.dis ?? GameConst.Battle.DEFAULT_SEARCH_RANGE);
// const enemyPositions = this.findNearbyEnemies(caster, heroAttrs.fac, range);
// // 选择最多maxTargets个目标
// for (let i = 0; i < Math.min(maxTargets, enemyPositions.length); i++) {
// targets.push(enemyPositions[i]);
// }
// // 如果没有找到敌人,使用默认位置
// if (targets.length === 0) {
// targets.push(...this.sDefaultTargets(caster, heroAttrs.fac));
// }
// return targets;
// }
// /**
// * 选择默认目标
// */
// private sDefaultTargets(caster: HeroViewComp, fac: number): Vec3[] {
// const targets: Vec3[] = [];
// const defaultX = fac === 0 ? GameConst.Battle.DEFAULT_TARGET_X_RIGHT : GameConst.Battle.DEFAULT_TARGET_X_LEFT;
// targets.push(v3(defaultX, BoxSet.GAME_LINE, GameConst.Battle.DEFAULT_TARGET_Z));
// return targets;
// }
// /**
// * 查找附近的敌人
// */
// private findNearbyEnemies(caster: HeroViewComp, fac: number, range: number): Vec3[] {
// const enemies: Vec3[] = [];
// if (!caster || !caster.node) return enemies;
// const currentPos = caster.node.position;
// const results: { pos: Vec3; dist: number; laneBias: number }[] = [];
// ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
// const model = e.get(HeroAttrsComp);
// const view = e.get(HeroViewComp);
// if (!model || !view || !view.node) return false;
// if (model.is_dead) return false;
// if (model.fac === fac) return false;
// const pos = view.node.position.clone();
// pos.y += GameConst.Battle.SEARCH_Y_OFFSET;
// const dist = Math.abs(currentPos.x - pos.x);
// if (dist <= range) {
// const laneBias = Math.abs(currentPos.y - pos.y);
// results.push({ pos: pos, dist, laneBias });
// }
// return false;
// });
// results.sort((a, b) => {
// if (a.laneBias !== b.laneBias) return a.laneBias - b.laneBias;
// return a.dist - b.dist;
// });
// for (const r of results) enemies.push(r.pos);
// return enemies;
// }
// /**
// * 检查技能攻击范围内是否有敌人
// */
// private hasEnemyInSkillRange(heroView: HeroViewComp, heroAttrs: HeroAttrsComp, skillDistance: number): boolean {
// if (!heroView || !heroView.node) return false;
// const currentPos = heroView.node.position;
// const team = heroAttrs.fac;
// let found = false;
// ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
// const model = e.get(HeroAttrsComp);
// const view = e.get(HeroViewComp);
// if (!view || !view.node) return false;
// const distance = Math.abs(currentPos.x - view.node.position.x);
// if (model.fac !== team && !model.is_dead) {
// if (distance <= skillDistance) {
// found = true;
// return true;
// }
// }
// });
// return found;
// }
// /**
// * 检查技能范围内是否有友军
// */
// private hasTeamInSkillRange(heroView: HeroViewComp, heroAttrs: HeroAttrsComp, skillDistance: number): boolean {
// if (!heroView || !heroView.node) return false;
// const currentPos = heroView.node.position;
// const team = heroAttrs.fac;
// let found = false;
// ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
// const model = e.get(HeroAttrsComp);
// const view = e.get(HeroViewComp);
// if (!view || !view.node) return false;
// const distance = Math.abs(currentPos.x - view.node.position.x);
// if (model.fac === team && !model.is_dead) {
// if (distance <= skillDistance) {
// found = true;
// return true;
// }
// }
// });
// return found;
// }
// /**
// * 检查Buff技能是否有目标
// */
// private hasBuffTarget(heroView: HeroViewComp, heroAttrs: HeroAttrsComp, skillDistance: number, tGroup: TGroup): boolean {
// if (tGroup === TGroup.Self) return true; // 自身Buff总是可以释放
// // 如果是团队Buff检查范围内是否有队友
// if (tGroup === TGroup.Team || tGroup === TGroup.Ally) {
// return this.hasTeamInSkillRange(heroView, heroAttrs, skillDistance);
// }
// return false;
// }
// /**
// * 执行Buff技能
// */
// private executeBuffSkill(casterEntity: ecs.Entity, s_uuid: number, heroView: HeroViewComp, hset: HSSet): boolean {
// const hAttrsCom = casterEntity.get(HeroAttrsComp);
// const config = SkillSet[s_uuid];
// if (!config || !config.buffs || config.buffs.length === 0) return false;
// const targets = this.sBuffTargets(casterEntity, heroView, hAttrsCom, config);
// if (targets.length === 0) return false;
// const delay = GameConst.Battle.SKILL_CAST_DELAY;
// heroView.scheduleOnce(() => {
// for (const targetEntity of targets) {
// const targetAttrs = targetEntity.get(HeroAttrsComp);
// if (!targetAttrs) continue;
// // 应用所有配置的Buff
// for (const buffConf of config.buffs) {
// // 检查概率
// if (buffConf.chance >= 1 || Math.random() < buffConf.chance) {
// targetAttrs.addBuff(buffConf);
// mLogger.log(this.debugMode, 'SACastSystem', `[SACastSystem] Buff生效: 施法者=${casterEntity.get(HeroAttrsComp)?.hero_name}, 技能=${config.name}, 目标=${targetAttrs.hero_name}, Buff类型=${buffConf.buff}, 值=${buffConf.value}`);
// }
// }
// }
// }, delay);
// return true;
// }
// /**
// * 选择Buff目标
// */
// private sBuffTargets(casterEntity: ecs.Entity, casterView: HeroViewComp, heroAttrs: HeroAttrsComp, config: SkillConfig): ecs.Entity[] {
// const targets: ecs.Entity[] = [];
// const tGroup = config.TGroup;
// // 1. 自身
// if (tGroup === TGroup.Self) {
// targets.push(casterEntity);
// return targets;
// }
// // 2. 团队/友军
// if (tGroup === TGroup.Team || tGroup === TGroup.Ally) {
// const maxTargets = Math.max(GameConst.Skill.MIN_TARGET_COUNT, Number(config.t_num ?? 1));
// const range = Number(config.dis ?? GameConst.Battle.DEFAULT_SEARCH_RANGE);
// ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).forEach(e => {
// const model = e.get(HeroAttrsComp);
// const view = e.get(HeroViewComp);
// if (!model || !view || !view.node) return;
// if (model.fac !== heroAttrs.fac) return; // 必须是同阵营
// if (model.is_dead) return;
// const distance = Math.abs(casterView.node.position.x - view.node.position.x);
// if (distance <= range) {
// targets.push(e);
// }
// });
// return targets.slice(0, maxTargets);
// }
// return targets;
// }
// /**
// * 执行治疗技能
// */
// private executeHealSkill(casterEntity: ecs.Entity, s_uuid: number, heroView: HeroViewComp, hset: HSSet): boolean {
// const hAttrsCom = casterEntity.get(HeroAttrsComp);
// const config = SkillSet[s_uuid];
// if (!config) return false;
// const targets = this.sHealTargets(heroView, hAttrsCom, config);
// if (targets.length === 0) return false;
// const healAmount = config.ap * hAttrsCom.Attrs[Attrs.HP_MAX]/100;
// const delay = GameConst.Battle.SKILL_CAST_DELAY;
// heroView.scheduleOnce(() => {
// for (const targetEntity of targets) {
// const targetAttrs = targetEntity.get(HeroAttrsComp);
// const targetView = targetEntity.get(HeroViewComp);
// if (!targetAttrs || !targetView) continue;
// targetAttrs.add_hp(healAmount, true);
// targetView.health(healAmount);
// mLogger.log(this.debugMode, 'SACastSystem', `[SACastSystem] 治疗生效: 施法者=${casterEntity.get(HeroAttrsComp)?.hero_name}, 技能=${config.name}, 目标=${targetAttrs.hero_name}, 治疗量=${healAmount}`);
// }
// }, delay);
// return true;
// }
// /**
// * 执行护盾技能
// */
// private executeShieldSkill(casterEntity: ecs.Entity, s_uuid: number, heroView: HeroViewComp, hset: HSSet): boolean {
// const hAttrsCom = casterEntity.get(HeroAttrsComp);
// const config = SkillSet[s_uuid];
// if (!config) return false;
// const targets = this.sShieldTargets(heroView, hAttrsCom, config);
// if (targets.length === 0) return false;
// const shieldAmount = config.ap * hAttrsCom.Attrs[Attrs.HP_MAX]/100;
// const delay = GameConst.Battle.SKILL_CAST_DELAY;
// heroView.scheduleOnce(() => {
// for (const targetEntity of targets) {
// const targetAttrs = targetEntity.get(HeroAttrsComp);
// const targetView = targetEntity.get(HeroViewComp);
// if (!targetAttrs || !targetView) continue;
// targetAttrs.add_shield(shieldAmount, true);
// targetView.add_shield(shieldAmount);
// mLogger.log(this.debugMode, 'SACastSystem', `[SACastSystem] 护盾生效: 施法者=${casterEntity.get(HeroAttrsComp)?.hero_name}, 技能=${config.name}, 目标=${targetAttrs.hero_name}, 护盾量=${shieldAmount}`);
// }
// }, delay);
// return true;
// }
// /**
// * 选择治疗目标
// */
// private sHealTargets(caster: HeroViewComp, heroAttrs: HeroAttrsComp, config: SkillConfig): ecs.Entity[] {
// const targets: ecs.Entity[] = [];
// const maxTargets = Math.max(GameConst.Skill.MIN_TARGET_COUNT, Number(config.t_num ?? 1));
// const range = Number(config.dis ?? GameConst.Battle.DEFAULT_SEARCH_RANGE);
// ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).forEach(e => {
// const model = e.get(HeroAttrsComp);
// const view = e.get(HeroViewComp);
// if (!model || !view || !view.node) return;
// if (model.fac !== heroAttrs.fac) return;
// if (model.is_dead) return;
// const distance = Math.abs(caster.node.position.x - view.node.position.x);
// if (distance <= range) {
// targets.push(e);
// }
// });
// targets.sort((a, b) => {
// const attrsA = a.get(HeroAttrsComp);
// const attrsB = b.get(HeroAttrsComp);
// if (!attrsA || !attrsB) return 0;
// return attrsA.hp - attrsB.hp;
// });
// return targets.slice(0, maxTargets);
// }
// /**
// * 选择护盾目标
// */
// private sShieldTargets(caster: HeroViewComp, heroAttrs: HeroAttrsComp, config: SkillConfig): ecs.Entity[] {
// const targets: ecs.Entity[] = [];
// const maxTargets = Math.max(GameConst.Skill.MIN_TARGET_COUNT, Number(config.t_num ?? 1));
// const range = Number(config.dis ?? GameConst.Battle.DEFAULT_SEARCH_RANGE);
// ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).forEach(e => {
// const model = e.get(HeroAttrsComp);
// const view = e.get(HeroViewComp);
// if (!model || !view || !view.node) return;
// if (model.fac !== heroAttrs.fac) return;
// if (model.is_dead) return;
// const distance = Math.abs(caster.node.position.x - view.node.position.x);
// if (distance <= range) {
// targets.push(e);
// }
// });
// return targets.slice(0, maxTargets);
// }
// /**
// * 根据位置查找实体
// */
// private findEntityAtPosition(pos: Vec3): ecs.Entity | null {
// let foundEntity: ecs.Entity | null = null;
// ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
// const view = e.get(HeroViewComp);
// if (!view || !view.node) return false;
// const distance = Vec3.distance(pos, view.node.position);
// if (distance < 50) {
// foundEntity = e;
// return true;
// }
// return false;
// });
// return foundEntity;
// }
// }

View File

@@ -1,9 +0,0 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "e76866cc-7379-436f-91ff-e71e790580a9",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -166,9 +166,12 @@ export class SkillView extends CCComp {
// 清理碰撞体事件监听
if (this.collider) {
this.collider.off(Contact2DType.BEGIN_CONTACT);
this.collider.enabled = false;
}
// 取消所有定时器
this.unscheduleAllCallbacks();
this.node.destroy();
if (this.node && this.node.isValid) {
this.node.active = false;
}
}
}
}