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