diff --git a/assets/script/game/map/MissionHeroComp.ts b/assets/script/game/map/MissionHeroComp.ts index 618304cd..159ea800 100644 --- a/assets/script/game/map/MissionHeroComp.ts +++ b/assets/script/game/map/MissionHeroComp.ts @@ -21,10 +21,10 @@ * revive 系技能为"濒死回血"(血量归零时消耗次数回血续命),不触发真死。 * * 出生点规则: - * 按职业(HRole)分三排:战士/坦克 → 前排(x=-200,最靠右迎敌), - * 射手/刺客 → 中排(x=-260),辅助/法师 → 后排(x=-320,最靠左)。 - * 同一职业重复召唤时,从该职业基准点向两侧 ±SLOT_OFFSET 依次交替展开,避免落点重叠。 - * posIndex 取 heroGrid 全局空闲下标(0~9)用于网格登记/索敌,落点偏移与 posIndex 解耦。 + * 按攻击类型(HType)分三排:近战 → 前排(x=-200,最靠右迎敌), + * 中距 → 中排(x=-260),远程 → 后排(x=-320,最靠左)。 + * 同一类型重复召唤时,从该类型基准点向两侧 ±SLOT_OFFSET 依次交替展开,避免落点重叠。 + * posIndex 取 heroGrid 全局空闲下标(0~9)用于网格登记/索敌,落点偏移与 posIndex、职业均解耦。 * * 依赖: * - Hero(hero/Hero.ts)—— 英雄 ECS 实体类 @@ -39,7 +39,7 @@ import { Hero } from "../hero/Hero"; import { smc } from "../common/SingletonModuleComp"; import { Timer } from "db://oops-framework/core/common/timer/Timer"; import { GameEvent } from "../common/config/GameEvent"; -import { HeroInfo, HRole } from "../common/config/heroSet"; +import { HeroInfo, HType } from "../common/config/heroSet"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; import { HeroAttrsComp } from "../hero/HeroAttrsComp"; import { FacSet, FightSet, BoxSet } from "../common/config/GameSet"; @@ -80,19 +80,16 @@ export class MissionHeroComp extends CCComp { ]; /** - * 职业 → 出生排映射: - * 战士/坦克 → 前排,射手/刺客 → 中排,辅助/法师 → 后排。 + * 攻击类型 → 出生排映射: + * 近战 → 前排,中距 → 中排,远程 → 后排。 */ - private static readonly ROLE_ROW: Record = { - [HRole.Tank]: MissionHeroComp.HERO_ROW_FRONT, - [HRole.Warrior]: MissionHeroComp.HERO_ROW_FRONT, - [HRole.Assassin]: MissionHeroComp.HERO_ROW_MID, - [HRole.Archer]: MissionHeroComp.HERO_ROW_MID, - [HRole.Mage]: MissionHeroComp.HERO_ROW_BACK, - [HRole.Support]: MissionHeroComp.HERO_ROW_BACK, + private static readonly TYPE_ROW: Record = { + [HType.Melee]: MissionHeroComp.HERO_ROW_FRONT, + [HType.Mid]: MissionHeroComp.HERO_ROW_MID, + [HType.Long]: MissionHeroComp.HERO_ROW_BACK, }; - /** 同职业重复召唤时的落点横向间隔(像素) */ + /** 同类型重复召唤时的落点横向间隔(像素) */ private static readonly SLOT_OFFSET = 30 /** 英雄出生时的掉落高度(从空中落到地面的像素差) */ @@ -232,35 +229,34 @@ export class MissionHeroComp extends CCComp { /** * 为英雄分配出生槽位: - * 按职业取三排基准点(战士/坦克前排、射手/刺客中排、辅助/法师后排), - * 同职业第 1 个落基准点,之后按 ±SLOT_OFFSET 交替展开(0, +20, -20, +40, -40 …)。 + * 按攻击类型取三排基准点(近战前排、中距中排、远程后排), + * 同类型第 1 个落基准点,之后按 ±SLOT_OFFSET 交替展开(0, +30, -30, +60, -60 …)。 * - * 槽位编码:posIndex 直接取 heroGrid 的全局空闲下标(0~9),不做 role 分段, - * 避免 role*10 编码超出 heroGrid 容量(长度 10)导致非坦克职业全部越界兜底。 - * 落点偏移由「同职业已占位英雄数」推展开序 n 计算,与 posIndex 解耦。 + * 槽位编码:posIndex 直接取 heroGrid 的全局空闲下标(0~9),与落点无映射。 + * 落点偏移由「同攻击类型已占位英雄的 x 偏移」推展开序计算,与 posIndex、职业均解耦。 * - * @param role 英雄职业(决定所属排) + * @param type 英雄攻击类型(HType,决定所属排) * @param heroes 当前存活英雄列表(spawnHeroAt 在实体创建前快照,避免复用池残留实体干扰槽位判定) * @returns { posIndex heroGrid 空闲下标(-1 表示满员未登记), landingPos 落地点 } */ - private pickSpawnSlotForHero(role: HRole, heroes: Hero[]): { posIndex: number; landingPos: Vec3 } { - const basePos = MissionHeroComp.ROLE_ROW[role] ?? MissionHeroComp.HERO_ROW_MID; + private pickSpawnSlotForHero(type: HType, heroes: Hero[]): { posIndex: number; landingPos: Vec3 } { + const basePos = MissionHeroComp.TYPE_ROW[type] ?? MissionHeroComp.HERO_ROW_MID; - // 同职业已占位的展开序集合(依据落点 x 反推,与 posIndex 解耦) + // 同攻击类型已占位的 x 偏移集合(依据落点 x 反推,与 posIndex 解耦) const usedOffsets = new Set(); const occupiedGrid = new Set(); for (const h of heroes) { const m = h.get(HeroAttrsComp)!; if (m.posIndex >= 0) occupiedGrid.add(m.posIndex); - if (m.role !== role) continue; + if (m.type !== type) continue; const view = h.get(HeroViewComp); if (view?.node?.isValid) { usedOffsets.add(Math.round(view.node.position.x - basePos.x)); } } - // 展开序 n:0=居中,1=+20,2=-20,3=+40 …,找到第一个未被同职业占用的偏移 - // 上限取 heroGrid 容量,同职业超过该数量时复用基准点(不再向外展开) + // 展开序 n:0=居中,1=+30,2=-30,3=+60 …,找到第一个未被同类型占用的偏移 + // 上限取 heroGrid 容量,同类型超过该数量时复用基准点(不再向外展开) let offsetX = 0; for (let n = 0; n < smc.mission.heroGrid.length; n++) { const step = Math.ceil(n / 2); @@ -306,19 +302,19 @@ export class MissionHeroComp extends CCComp { private spawnHeroAt(uuid: number, hero_lv: number, card_lv: number, posIndex: number, inheritPos: Vec3 | null = null) { let hero = ecs.getEntity(Hero); let scale = 1 - const role = HeroInfo[uuid]?.role ?? HRole.Warrior; + const type = HeroInfo[uuid]?.type ?? HType.Mid; let finalPosIndex = posIndex; let landingPos: Vec3; if (inheritPos) { // 合成继承:沿用旧英雄节点位置,posIndex 透传登记网格 - landingPos = v3(inheritPos.x, MissionHeroComp.ROLE_ROW[role]?.y ?? BoxSet.GAME_LINE, 0); + landingPos = v3(inheritPos.x, MissionHeroComp.TYPE_ROW[type]?.y ?? BoxSet.GAME_LINE, 0); } else { const heroes = this.getAllHeroes().filter(h => { const m = h.get(HeroAttrsComp); return !!m && !m.is_dead; }); - const slot = this.pickSpawnSlotForHero(role, heroes); + const slot = this.pickSpawnSlotForHero(type, heroes); finalPosIndex = slot.posIndex; landingPos = slot.landingPos; }