- 移除 Skill.ts 中未使用的 wfuny 属性赋值 - 将 SCastSystem 中的目标选择逻辑重构为统一方法 - 在施放技能前增加目标有效性检查,避免无效操作 - 移除 HeroAtkSystem 中未使用的导入和接口字段 - 调整 SkillSet 中技能 6008 的 ready 参数值
236 lines
8.1 KiB
TypeScript
236 lines
8.1 KiB
TypeScript
import { BoxCollider2D, instantiate, Node, Prefab, v3, Vec3, NodePool } from "cc";
|
||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||
import { EType, SkillSet } from "../common/config/SkillSet";
|
||
import { oops } from "db://oops-framework/core/Oops";
|
||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||
import { Attrs } from "../common/config/HeroAttrs";
|
||
|
||
import { SkillView } from "./SkillView";
|
||
import { SDataCom } from "./SDataCom";
|
||
import { SMoveDataComp } from "../skill/SMoveComp";
|
||
import { StimeDataComp } from "./STimeComp";
|
||
import { HeroViewComp } from "../hero/HeroViewComp";
|
||
import { smc } from "../common/SingletonModuleComp";
|
||
import { mLogger } from "../common/Logger";
|
||
|
||
/** Skill 模块 */
|
||
@ecs.register(`Skill`)
|
||
export class Skill extends ecs.Entity {
|
||
private debugMode: boolean = false;
|
||
/** 多键对象池:Map<prefabPath, NodePool> */
|
||
static pools: Map<string, NodePool> = new Map();
|
||
static readonly MAX_POOL_SIZE: number = 64;
|
||
|
||
static getFromPool(path: string): Node | null {
|
||
if (this.pools.has(path)) {
|
||
const pool = this.pools.get(path)!;
|
||
while (pool.size() > 0) {
|
||
const node = pool.get();
|
||
if (node && node.isValid) {
|
||
return node;
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
static putToPool(path: string, node: Node) {
|
||
if (!node || !node.isValid) return;
|
||
if (!this.pools.has(path)) {
|
||
this.pools.set(path, new NodePool());
|
||
}
|
||
const pool = this.pools.get(path)!;
|
||
if (pool.size() >= this.MAX_POOL_SIZE) {
|
||
node.destroy();
|
||
return;
|
||
}
|
||
pool.put(node);
|
||
}
|
||
|
||
static clearPools() {
|
||
this.pools.forEach((pool) => {
|
||
pool.clear();
|
||
});
|
||
this.pools.clear();
|
||
}
|
||
|
||
static getPoolStats() {
|
||
let total = 0;
|
||
this.pools.forEach((pool) => {
|
||
total += pool.size();
|
||
});
|
||
return {
|
||
paths: this.pools.size,
|
||
total,
|
||
maxPerPath: this.MAX_POOL_SIZE
|
||
};
|
||
}
|
||
|
||
/** ---------- 数据层 ---------- */
|
||
SDataCom!: SDataCom;
|
||
SMoveCom!: SMoveDataComp
|
||
|
||
/** 当前技能的预制体路径(用于对象池回收) */
|
||
private prefabPath: string = "";
|
||
private skillNode: Node | null = null; // 持有节点引用以便销毁时回收
|
||
|
||
/** ---------- 业务层 ---------- */
|
||
// SkillBll!: SkillBllComp;
|
||
|
||
/** ---------- 视图层 ---------- */
|
||
SView!: SkillView;
|
||
|
||
/** 实始添加的数据层组件 */
|
||
protected init() {
|
||
this.addComponents<SDataCom>(SDataCom);
|
||
this.addComponents<SMoveDataComp>(SMoveDataComp);
|
||
}
|
||
load(startPos: Vec3, parent: Node, s_uuid: number, targetPos: Vec3,
|
||
caster:HeroViewComp,ext_dmg:number=0) {
|
||
const config = SkillSet[s_uuid];
|
||
|
||
if (!config) {
|
||
mLogger.error(this.debugMode, 'Skill', "[Skill] 技能配置不存在:", s_uuid);
|
||
return;
|
||
}
|
||
|
||
// 加载预制体
|
||
const path = `game/skill/atk/${config.sp_name}`;
|
||
const prefab:Prefab = oops.res.get(path, Prefab);
|
||
if (!prefab) {
|
||
mLogger.error(this.debugMode, 'Skill', "[Skill] 预制体加载失败:", path);
|
||
return;
|
||
}
|
||
const node: Node = Skill.getFromPool(path) || instantiate(prefab);
|
||
if (!node || !node.isValid) {
|
||
mLogger.error(this.debugMode, 'Skill', "[Skill] 节点无效:", path);
|
||
return;
|
||
}
|
||
|
||
this.prefabPath = path;
|
||
this.skillNode = node;
|
||
|
||
let skillParent: Node | null = null;
|
||
if (smc.map && smc.map.MapView && smc.map.MapView.scene && smc.map.MapView.scene.entityLayer && smc.map.MapView.scene.entityLayer.node) {
|
||
skillParent = smc.map.MapView.scene.entityLayer.node.getChildByName("SKILL");
|
||
}
|
||
if (!skillParent || !skillParent.isValid) {
|
||
skillParent = parent;
|
||
}
|
||
|
||
if (!skillParent || !skillParent.isValid) {
|
||
mLogger.error(this.debugMode, 'Skill', "[Skill] 父节点无效");
|
||
if(node.isValid) node.destroy();
|
||
return;
|
||
}
|
||
|
||
node.parent = skillParent;
|
||
node.active = true;
|
||
// 设置节点属性
|
||
let face=caster.node.scale.x < 0 ? -1 : 1
|
||
node.setScale(v3(node.scale.x*face,node.scale.y,1))
|
||
// 初始视图
|
||
const SView = node.getComponent(SkillView);
|
||
if (!SView) {
|
||
mLogger.error(this.debugMode, 'Skill', "[Skill] SkillView 组件缺失:", path);
|
||
if (node.isValid) node.destroy();
|
||
return;
|
||
}
|
||
if(config.EType!=EType.collision){
|
||
const collider=node.getComponent(BoxCollider2D);
|
||
if(collider){
|
||
collider.enabled=false
|
||
}
|
||
}
|
||
|
||
// 只设置必要的运行时属性,配置信息通过 SkillSet[uuid] 访问
|
||
// 核心标识
|
||
SView.s_uuid= s_uuid
|
||
SView.group= caster.box_group
|
||
|
||
this.add(SView);
|
||
startPos.x=startPos.x+SView.atk_x*face
|
||
startPos.y=startPos.y+SView.atk_y
|
||
|
||
node.setPosition(startPos);
|
||
|
||
// 初始化移动组件 - 从SkillView获取移动参数
|
||
let sMoveCom = this.get(SMoveDataComp);
|
||
if (!sMoveCom) {
|
||
sMoveCom = this.add(SMoveDataComp);
|
||
}
|
||
sMoveCom.reset(); // 复用组件时重置状态
|
||
sMoveCom.startPos.set(startPos);
|
||
sMoveCom.targetPos.set(targetPos);
|
||
sMoveCom.s_uuid = s_uuid;
|
||
sMoveCom.scale = caster.node.scale.x < 0 ? -1 : 1;
|
||
sMoveCom.runType = config.RType;
|
||
sMoveCom.endType = config.EType;
|
||
// 从SkillView获取移动参数,位置初始化由SMoveSystem统一处理
|
||
sMoveCom.atk_x = SView.atk_x;
|
||
sMoveCom.atk_y = SView.atk_y;
|
||
|
||
if (config.EType === EType.timeEnd) {
|
||
let sTimeCom = this.get(StimeDataComp);
|
||
if (!sTimeCom) sTimeCom = this.add(StimeDataComp);
|
||
sTimeCom.reset();
|
||
sTimeCom.s_uuid = s_uuid;
|
||
sTimeCom.totalTime = Math.max(1, config.time ?? 0);
|
||
sTimeCom.hitInterval = Math.max(0.5, config.hitcd || 0);
|
||
} else {
|
||
const sTimeCom = this.get(StimeDataComp);
|
||
if (sTimeCom) this.remove(StimeDataComp);
|
||
}
|
||
|
||
let cAttrsComp=caster.ent.get(HeroAttrsComp)
|
||
// 初始化数据组件
|
||
let sDataCom = this.get(SDataCom);
|
||
if (!sDataCom) {
|
||
sDataCom = this.add(SDataCom);
|
||
}
|
||
sDataCom.reset();
|
||
sDataCom.group=caster.box_group
|
||
sDataCom.caster=caster
|
||
sDataCom.casterEid=caster.ent.eid
|
||
sDataCom.Attrs = {};
|
||
sDataCom.Attrs[Attrs.ap] = cAttrsComp.ap;
|
||
sDataCom.Attrs[Attrs.critical] = cAttrsComp.critical;
|
||
sDataCom.Attrs[Attrs.critical_dmg] = cAttrsComp.critical_dmg;
|
||
sDataCom.Attrs[Attrs.freeze_chance] = cAttrsComp.freeze_chance;
|
||
sDataCom.Attrs[Attrs.stun_chance] = cAttrsComp.stun_chance;
|
||
sDataCom.Attrs[Attrs.back_chance] = cAttrsComp.back_chance;
|
||
sDataCom.Attrs[Attrs.slow_chance] = cAttrsComp.slow_chance;
|
||
sDataCom.Attrs[Attrs.puncture] = cAttrsComp.puncture;
|
||
sDataCom.Attrs[Attrs.puncture_dmg] = cAttrsComp.puncture_dmg;
|
||
|
||
sDataCom.s_uuid=s_uuid
|
||
sDataCom.fac=cAttrsComp.fac
|
||
sDataCom.ext_dmg=ext_dmg
|
||
sDataCom.hit_count = 0
|
||
sDataCom.max_hit_count = Math.max(1, config.hit_count + cAttrsComp.puncture)
|
||
SView.init();
|
||
}
|
||
|
||
/** 模块资源释放 */
|
||
destroy() {
|
||
// 注: 自定义释放逻辑,视图层实现 ecs.IComp 接口的 ecs 组件需要手动释放
|
||
this.remove(SDataCom);
|
||
this.remove(SMoveDataComp)
|
||
this.remove(StimeDataComp)
|
||
|
||
// 移除组件会触发 reset,但我们在 reset 中移除了 destroy
|
||
this.remove(SkillView)
|
||
|
||
// 回收节点到对象池
|
||
if (this.skillNode && this.skillNode.isValid && this.prefabPath) {
|
||
Skill.putToPool(this.prefabPath, this.skillNode);
|
||
this.skillNode = null;
|
||
}
|
||
|
||
super.destroy();
|
||
|
||
}
|
||
}
|
||
|
||
|