import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { HeroViewComp } from "./HeroViewComp"; import { HeroAttrsComp } from "./HeroAttrsComp"; import { smc } from "../common/SingletonModuleComp"; import { FacSet } from "../common/config/GameSet"; import { HeroDisVal, HType } from "../common/config/heroSet"; import { Node } from "cc"; @ecs.register('MonMoveComp') export class MonMoveComp extends ecs.Comp { /** 朝向:1=向右,-1=向左 */ direction: number = -1; /** 当前移动目标 X */ targetX: number = 0; /** 是否允许移动(出生落地前会短暂关闭) */ moving: boolean = true; /** 站位基准 Y */ baseY: number = 0; /** 出生序,用于同条件渲染排序稳定 */ spawnOrder: number = 0; reset() { this.direction = -1; this.targetX = 0; this.moving = true; this.baseY = 0; this.spawnOrder = 0; } } @ecs.register('MonMoveSystem') export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { /** 渲染层级重排节流,避免每帧排序 */ private readonly renderSortInterval = 0.05; private lastRenderSortAt = 0; private monMoveMatcher: ecs.IMatcher | null = null; private heroViewMatcher: ecs.IMatcher | null = null; private readonly renderEntries: { node: Node; bossPriority: number; frontScore: number; spawnOrder: number; eid: number }[] = []; private renderEntryCount = 0; private getMonMoveMatcher(): ecs.IMatcher { if (!this.monMoveMatcher) { this.monMoveMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp, MonMoveComp); } return this.monMoveMatcher; } private getHeroViewMatcher(): ecs.IMatcher { if (!this.heroViewMatcher) { this.heroViewMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp); } return this.heroViewMatcher; } filter(): ecs.IMatcher { return ecs.allOf(MonMoveComp, HeroViewComp, HeroAttrsComp); } update(e: ecs.Entity) { /** 战斗未开始/暂停时不驱动移动 */ if (!smc.mission.play || smc.mission.pause) return; const model = e.get(HeroAttrsComp); const move = e.get(MonMoveComp); const view = e.get(HeroViewComp); if (!model || !move || !view || !view.node) return; if (model.fac !== FacSet.MON) return; if (!move.moving) return; /** 关卡阶段性冻结怪物行为 */ if (smc.mission.stop_mon_action) { this.clearCombatTarget(model); view.status_change("idle"); return; } if (model.is_stop || model.is_dead || model.is_reviving || model.isFrost()) { this.clearCombatTarget(model); if (!model.is_reviving) view.status_change("idle"); return; } /** 所有移动都锁定在 baseY,避免出现“漂移” */ // 注意:不再在 MonMoveComp 强行重置 X 轴坐标,避免与其他表现逻辑冲突 if (view.node.position.y !== move.baseY) { view.node.setPosition(view.node.position.x, move.baseY, 0); } // 渲染层级统交由 MoveSystem 统一处理,避免两个 System 争抢 setSiblingIndex // 仅在战斗中才处理索敌 if (!smc.mission.in_fight) return; const nearestEnemy = this.findNearestEnemy(e); if (nearestEnemy) { /** 有敌人:进入固定位置攻击逻辑 */ this.processCombatLogic(e, move, view, model, nearestEnemy); this.syncCombatTarget(model, view, nearestEnemy); } else { /** 无敌人:原地待机 */ this.clearCombatTarget(model); model.is_atking = false; view.status_change("idle"); } } private clearCombatTarget(model: HeroAttrsComp): void { model.combat_target_eid = -1; model.enemy_in_cast_range = false; } private syncCombatTarget(model: HeroAttrsComp, selfView: HeroViewComp, enemyView: HeroViewComp): void { if (!enemyView || !enemyView.node || !enemyView.ent) { this.clearCombatTarget(model); return; } const enemyAttrs = enemyView.ent.get(HeroAttrsComp); if (!enemyAttrs || enemyAttrs.is_dead || enemyAttrs.is_reviving || enemyAttrs.fac === model.fac) { this.clearCombatTarget(model); return; } model.combat_target_eid = enemyView.ent.eid; model.enemy_in_cast_range = this.isEnemyInAttackRange(model, selfView.node.position.x, enemyView.node.position.x); } private isEnemyInAttackRange(model: HeroAttrsComp, selfX: number, enemyX: number): boolean { const dist = Math.abs(selfX - enemyX); const attackRange = model.dis; return dist <= attackRange; } private processCombatLogic(e: ecs.Entity, move: MonMoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) { const selfX = view.node.position.x; const enemyX = enemy.node.position.x; const inRange = this.isEnemyInAttackRange(model, selfX, enemyX); // 攻击判定:怪物在固定位置,如果在攻击范围内则攻击,否则原地待机 if (inRange) { model.is_atking = true; // 确保朝向敌人 const dir = enemyX > selfX ? 1 : -1; view.scale = dir; } else { model.is_atking = false; view.status_change("idle"); } } private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null { const currentView = entity.get(HeroViewComp); if (!currentView?.node) return null; const currentPos = currentView.node.position; const myFac = entity.get(HeroAttrsComp).fac; let nearest: HeroViewComp | null = null; let minDis = Infinity; /** 遍历筛出最近敌人:以 X 轴距离为主,Y 轴距离作为同排的决胜权重,使角色优先攻击同路的敌人 */ ecs.query(this.getHeroViewMatcher()).forEach(e => { const m = e.get(HeroAttrsComp); if (m.fac !== myFac && !m.is_dead) { const v = e.get(HeroViewComp); if (v?.node) { const dx = Math.abs(currentPos.x - v.node.position.x); const dy = Math.abs(currentPos.y - v.node.position.y); const d = dx + dy * 0.1; // Y轴权重较小,仅在 X 相近时起决定作用 if (d < minDis) { minDis = d; nearest = v; } } } }); return nearest; } }