Files
pixelheros/assets/script/game/hero/Mon.ts
walkpan 5d09b3361e feat(battle): 重构技能施放与战斗距离系统
- 新增技能距离缓存机制,根据英雄类型动态计算最小和最大攻击范围
- 重构SCastSystem实现完整的技能施放逻辑,支持伤害、治疗、护盾和buff技能
- 在Hero和Monster初始化时调用updateSkillDistanceCache预计算技能距离
- 修改HeroMoveSystem和MonMoveSystem使用动态战斗范围,支持撤退逻辑
- 优化Skill实体创建,增加对象池支持
- 添加技能CD触发方法和状态检查方法
2026-03-12 09:13:28 +08:00

183 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { instantiate, Node, Prefab, Vec3 ,v3,resources,SpriteFrame,Sprite,SpriteAtlas, BoxCollider2D, NodePool} from "cc";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { smc } from "../common/SingletonModuleComp";
import { BoxSet, FacSet, FightSet, IndexSet } from "../common/config/GameSet";
import { HeroInfo } from "../common/config/heroSet";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { BuffConf, SkillSet } from "../common/config/SkillSet";
import { getMonAttr, MonType } from "../map/RogueConfig";
import { HeroViewComp } from "./HeroViewComp";
import { MonMoveComp } from "./MonMove";
import { mLogger } from "../common/Logger";
/** 角色实体 */
@ecs.register(`Monster`)
export class Monster extends ecs.Entity {
HeroModel!: HeroAttrsComp;
HeroView!: HeroViewComp;
MonMove!: MonMoveComp;
private debugMode: boolean = false; // 是否启用调试模式
// 多键对象池Map<prefabPath, NodePool>
static pools: Map<string, NodePool> = new Map();
static getFromPool(path: string): Node | null {
if (this.pools.has(path)) {
const pool = this.pools.get(path)!;
if (pool.size() > 0) {
return pool.get();
}
}
return null;
}
static putToPool(path: string, node: Node) {
if (!this.pools.has(path)) {
this.pools.set(path, new NodePool());
}
this.pools.get(path)!.put(node);
}
protected init() {
this.addComponents<ecs.Comp>(
MonMoveComp,
HeroAttrsComp,
);
}
destroy(): void {
// 回收节点到对象池
const model = this.get(HeroAttrsComp);
const view = this.get(HeroViewComp);
if (model && view && view.node && view.node.isValid) {
const path = "game/heros/" + HeroInfo[model.hero_uuid].path;
Monster.putToPool(path, view.node);
}
this.remove(HeroViewComp);
this.remove(HeroAttrsComp);
super.destroy();
}
/** 加载角色 */
load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001,lv:number=1,monType:MonType=MonType.NORMAL, buffs: BuffConf[] = [],is_call=false, lane: number = 0, spawnOrder: number = 0, gameTime: number = 0) {
scale=-1
let size=1
var scene = smc.map.MapView.scene;
var path = "game/heros/"+HeroInfo[uuid].path;
// 尝试从池中获取
let node = Monster.getFromPool(path);
if (!node) {
var prefab: Prefab = oops.res.get(path, Prefab)!;
node = instantiate(prefab);
}
let LINE1=scene.entityLayer!.node!.getChildByName("LINE1")!;
let LINE2=scene.entityLayer!.node!.getChildByName("LINE2")!;
let LINE3=scene.entityLayer!.node!.getChildByName("LINE3")!;
let LINE4=scene.entityLayer!.node!.getChildByName("LINE4")!;
// 🔥 设置初始 SiblingIndex - 防止溢出
const baseLane = lane === 0 ? LINE1 : lane === 1 ? LINE2 : lane === 2 ? LINE3 : LINE4;
node.parent = baseLane
var view = node.getComponent(HeroViewComp)!;
const collider = node.getComponent(BoxCollider2D);
if (collider) {
// 先禁用,下一帧启用,确保物理系统能正确注册新激活的节点
collider.enabled = false;
view.scheduleOnce(() => {
if (node && node.isValid) {
collider.enabled = true;
collider.group = BoxSet.MONSTER; // 确保碰撞组正确
collider.apply(); // 强制应用更改(如果需要)
}
}, 0); // 0延迟等于下一帧执行
}
node.setScale(size*node.scale.x,size*node.scale.y);
node.setPosition(pos)
const model = this.get(HeroAttrsComp);
let hero = HeroInfo[uuid]; // 共用英雄数据
// 设置 View 层属性(表现相关)
view.scale = scale;
view.box_group = BoxSet.MONSTER;
// 设置 Model 层属性(数据相关)
model.hero_uuid = uuid;
model.hero_name = hero.name;
model.lv = lv;
model.type = hero.type;
model.fac = FacSet.MON;
model.is_boss = monType == MonType.BOSS;
if(model.is_boss) node.parent = scene.entityLayer!.node!.getChildByName("HERO")!;
if(!model.is_boss){
model.is_kalami = true;
}
// 根据等级和类型获取怪物属性(使用新的动态成长系统)
const {hp,ap, speed} = getMonAttr(lv, uuid, monType, gameTime);
// 初始化属性数组
model.hp = model.hp_max = hp;
model.ap = ap;
model.speed = speed; // 使用成长后的速度
model.a_cd_max=hero.as
model.s_cd_max=hero.ss
model.back_chance=FightSet.BACK_CHANCE
// ✅ 初始化技能数据(迁移到 HeroSkillsComp
if(hero.skills[0]) model.atk_id=hero.skills[0]
if(hero.skills[1]) model.skill_id=hero.skills[1]
model.updateSkillDistanceCache(model.skill_id || model.atk_id);
this.add(view);
// 重置视图状态(对象池复用时必须)
view.init();
oops.message.dispatchEvent("monster_load",this)
// 初始化移动参数,包括线路和生成顺序
const move = this.get(MonMoveComp);
move.reset();
move.direction = -1; // 向左移动
move.targetX = -800; // 左边界
move.lane = lane; // 设置线路标识
move.spawnOrder = spawnOrder; // 设置生成顺序
smc.vmdata.mission_data.mon_num++
}
reset() {
// 注: 自定义释放逻辑,视图层实现 ecs.IComp 接口的 ecs 组件需要手动释放
super.destroy();
}
}
@ecs.register('MonLifecycleSystem')
export class MonLifecycleSystem extends ecs.ComblockSystem
implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem {
debugMode: boolean = false; // 是否启用调试模式
filter() {
return ecs.allOf(MonMoveComp);
}
entityEnter(e: ecs.Entity): void {
// 怪物实体创建时的特殊处理
const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
mLogger.log(this.debugMode, 'MonLifecycleSystem', `怪物进入世界: ${heroAttrs.hero_name}`);
} else {
mLogger.log(this.debugMode, 'MonLifecycleSystem', `怪物进入世界: 实体ID ${e.eid}`);
}
}
entityRemove(e: ecs.Entity): void {
// 怪物实体销毁时的清理工作
const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
mLogger.log(this.debugMode, 'MonLifecycleSystem', `怪物离开世界: ${heroAttrs.hero_name}`);
} else {
mLogger.log(this.debugMode, 'MonLifecycleSystem', `怪物离开世界: 实体ID ${e.eid}`);
}
}
}