feat(rogue): 调整Boss占用槽位为3格并重构波次配置

- 将Boss默认占用槽位从2格改为3格,允许在任意连续三格空闲位置放置
- 简化波次配置,第1波改为2近战+1近战Boss,第2波改为2近战+1远程Boss
- 更新怪物池配置,调整近战Boss池包含6006和6105
- 重构怪物分配逻辑,统一处理所有类型怪物的槽位分配
- 优化远程Boss的放置策略,优先从后往前寻找空闲槽位
This commit is contained in:
panw
2026-04-08 10:31:20 +08:00
parent 4a6f19ae8b
commit e7b0d55e36
2 changed files with 53 additions and 62 deletions

View File

@@ -10,7 +10,7 @@
*
* 关键设计:
* - 全场固定 5 个槽位(索引 0-4每个槽位占固定 X 坐标。
* - Boss 占 2 个连续槽位,只能放在 0、2 号位
* - Boss 默认3 个连续槽位,只要有连续三格空闲即可
* - slotOccupiedEids 记录每个槽位占用的怪物 ECS 实体 ID。
* - resetSlotSpawnData(wave) 在每波开始时读取配置,分配并立即生成所有怪物。
* - refreshSlotOccupancy() 定期检查槽位占用的实体是否仍存活,清除已死亡的占用。
@@ -177,14 +177,23 @@ export class MissionMonCompComp extends CCComp {
const item = this.MonQueue[0];
const isBoss = MonList[MonType.MeleeBoss].includes(item.uuid) || MonList[MonType.LongBoss].includes(item.uuid);
const slotsPerMon = isBoss ? 2 : 1;
const isLongBoss = MonList[MonType.LongBoss].includes(item.uuid);
const slotsPerMon = isBoss ? 3 : 1;
// 查找空闲槽位
let slotIndex = -1;
if (slotsPerMon === 2) {
// Boss 只能放在 0, 2需要连续 2 格空闲)
for (const idx of [0, 2]) {
if (!this.slotOccupiedEids[idx] && !this.slotOccupiedEids[idx + 1]) {
if (slotsPerMon === 3) {
// 构造可用索引列表
let allowedIndices = [];
for (let i = 0; i < MissionMonCompComp.MAX_SLOTS - 2; i++) {
allowedIndices.push(i);
}
// 远程 Boss 插队时优先尝试从后往前找
if (isLongBoss) {
allowedIndices.reverse();
}
for (const idx of allowedIndices) {
if (!this.slotOccupiedEids[idx] && !this.slotOccupiedEids[idx + 1] && !this.slotOccupiedEids[idx + 2]) {
slotIndex = idx;
break;
}
@@ -309,59 +318,53 @@ export class MissionMonCompComp extends CCComp {
const config: IWaveSlot[] = WaveSlotConfig[wave] || DefaultWaveSlot;
this.slotOccupiedEids = Array(MissionMonCompComp.MAX_SLOTS).fill(null);
let bosses: any[] = [];
let normals: any[] = [];
let allMons: any[] = [];
// 按类型分类
// 解析配置
for (const slot of config) {
const slotsPerMon = slot.slotsPerMon || 1;
const isBoss = slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss;
const slotsPerMon = slot.slotsPerMon || (isBoss ? 3 : 1);
for (let i = 0; i < slot.count; i++) {
const uuid = this.getRandomUuidByType(slot.type);
const upType = this.getRandomUpType();
const req = { uuid, isBoss, upType, monLv: wave, slotsPerMon };
if (isBoss || slotsPerMon === 2) {
bosses.push(req);
} else {
normals.push(req);
}
allMons.push(req);
}
}
this.waveTargetCount = bosses.length + normals.length;
this.waveTargetCount = allMons.length;
this.waveSpawnedCount = 0;
// Boss 优先分配(只能放在 0, 2
let bossAllowedIndices = [0, 2];
let assignedSlots = new Array(MissionMonCompComp.MAX_SLOTS).fill(null);
for (const boss of bosses) {
// 统一按顺序分配(根据所需格数找连续空位)
for (const mon of allMons) {
let placed = false;
for (const idx of bossAllowedIndices) {
if (!assignedSlots[idx] && !assignedSlots[idx + 1]) {
assignedSlots[idx] = boss;
assignedSlots[idx + 1] = "occupied"; // 占位标记
placed = true;
break;
if (mon.slotsPerMon === 3) {
// 需要 3 格,占满连续的 3 格
for (let idx = 0; idx < MissionMonCompComp.MAX_SLOTS - 2; idx++) {
if (!assignedSlots[idx] && !assignedSlots[idx + 1] && !assignedSlots[idx + 2]) {
assignedSlots[idx] = mon;
assignedSlots[idx + 1] = "occupied"; // 占位标记
assignedSlots[idx + 2] = "occupied"; // 占位标记
placed = true;
break;
}
}
} else {
// 只需要 1 格
for (let idx = 0; idx < MissionMonCompComp.MAX_SLOTS; idx++) {
if (!assignedSlots[idx]) {
assignedSlots[idx] = mon;
placed = true;
break;
}
}
}
if (!placed) {
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] No slot for boss!");
}
}
// 普通怪填充剩余空位
for (const normal of normals) {
let placed = false;
for (let i = 0; i < MissionMonCompComp.MAX_SLOTS; i++) {
if (!assignedSlots[i]) {
assignedSlots[i] = normal;
placed = true;
break;
}
}
if (!placed) {
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] No slot for normal monster!");
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] No slot for monster! uuid:", mon.uuid);
}
}