feat(rogue): 调整Boss占用槽位为3格并重构波次配置
- 将Boss默认占用槽位从2格改为3格,允许在任意连续三格空闲位置放置 - 简化波次配置,第1波改为2近战+1近战Boss,第2波改为2近战+1远程Boss - 更新怪物池配置,调整近战Boss池包含6006和6105 - 重构怪物分配逻辑,统一处理所有类型怪物的槽位分配 - 优化远程Boss的放置策略,优先从后往前寻找空闲槽位
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
* 关键设计:
|
* 关键设计:
|
||||||
* - 全场固定 5 个槽位(索引 0-4),每个槽位占固定 X 坐标。
|
* - 全场固定 5 个槽位(索引 0-4),每个槽位占固定 X 坐标。
|
||||||
* - Boss 占 2 个连续槽位,只能放在 0、2 号位。
|
* - Boss 默认占 3 个连续槽位,只要有连续三格空闲即可。
|
||||||
* - slotOccupiedEids 记录每个槽位占用的怪物 ECS 实体 ID。
|
* - slotOccupiedEids 记录每个槽位占用的怪物 ECS 实体 ID。
|
||||||
* - resetSlotSpawnData(wave) 在每波开始时读取配置,分配并立即生成所有怪物。
|
* - resetSlotSpawnData(wave) 在每波开始时读取配置,分配并立即生成所有怪物。
|
||||||
* - refreshSlotOccupancy() 定期检查槽位占用的实体是否仍存活,清除已死亡的占用。
|
* - refreshSlotOccupancy() 定期检查槽位占用的实体是否仍存活,清除已死亡的占用。
|
||||||
@@ -177,14 +177,23 @@ export class MissionMonCompComp extends CCComp {
|
|||||||
|
|
||||||
const item = this.MonQueue[0];
|
const item = this.MonQueue[0];
|
||||||
const isBoss = MonList[MonType.MeleeBoss].includes(item.uuid) || MonList[MonType.LongBoss].includes(item.uuid);
|
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;
|
let slotIndex = -1;
|
||||||
if (slotsPerMon === 2) {
|
if (slotsPerMon === 3) {
|
||||||
// Boss 只能放在 0, 2(需要连续 2 格空闲)
|
// 构造可用索引列表
|
||||||
for (const idx of [0, 2]) {
|
let allowedIndices = [];
|
||||||
if (!this.slotOccupiedEids[idx] && !this.slotOccupiedEids[idx + 1]) {
|
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;
|
slotIndex = idx;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -309,59 +318,53 @@ export class MissionMonCompComp extends CCComp {
|
|||||||
const config: IWaveSlot[] = WaveSlotConfig[wave] || DefaultWaveSlot;
|
const config: IWaveSlot[] = WaveSlotConfig[wave] || DefaultWaveSlot;
|
||||||
this.slotOccupiedEids = Array(MissionMonCompComp.MAX_SLOTS).fill(null);
|
this.slotOccupiedEids = Array(MissionMonCompComp.MAX_SLOTS).fill(null);
|
||||||
|
|
||||||
let bosses: any[] = [];
|
let allMons: any[] = [];
|
||||||
let normals: any[] = [];
|
|
||||||
|
|
||||||
// 按类型分类
|
// 解析配置
|
||||||
for (const slot of config) {
|
for (const slot of config) {
|
||||||
const slotsPerMon = slot.slotsPerMon || 1;
|
|
||||||
const isBoss = slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss;
|
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++) {
|
for (let i = 0; i < slot.count; i++) {
|
||||||
const uuid = this.getRandomUuidByType(slot.type);
|
const uuid = this.getRandomUuidByType(slot.type);
|
||||||
const upType = this.getRandomUpType();
|
const upType = this.getRandomUpType();
|
||||||
const req = { uuid, isBoss, upType, monLv: wave, slotsPerMon };
|
const req = { uuid, isBoss, upType, monLv: wave, slotsPerMon };
|
||||||
if (isBoss || slotsPerMon === 2) {
|
allMons.push(req);
|
||||||
bosses.push(req);
|
|
||||||
} else {
|
|
||||||
normals.push(req);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.waveTargetCount = bosses.length + normals.length;
|
this.waveTargetCount = allMons.length;
|
||||||
this.waveSpawnedCount = 0;
|
this.waveSpawnedCount = 0;
|
||||||
|
|
||||||
// Boss 优先分配(只能放在 0, 2)
|
|
||||||
let bossAllowedIndices = [0, 2];
|
|
||||||
let assignedSlots = new Array(MissionMonCompComp.MAX_SLOTS).fill(null);
|
let assignedSlots = new Array(MissionMonCompComp.MAX_SLOTS).fill(null);
|
||||||
|
|
||||||
for (const boss of bosses) {
|
// 统一按顺序分配(根据所需格数找连续空位)
|
||||||
|
for (const mon of allMons) {
|
||||||
let placed = false;
|
let placed = false;
|
||||||
for (const idx of bossAllowedIndices) {
|
|
||||||
if (!assignedSlots[idx] && !assignedSlots[idx + 1]) {
|
if (mon.slotsPerMon === 3) {
|
||||||
assignedSlots[idx] = boss;
|
// 需要 3 格,占满连续的 3 格
|
||||||
assignedSlots[idx + 1] = "occupied"; // 占位标记
|
for (let idx = 0; idx < MissionMonCompComp.MAX_SLOTS - 2; idx++) {
|
||||||
placed = true;
|
if (!assignedSlots[idx] && !assignedSlots[idx + 1] && !assignedSlots[idx + 2]) {
|
||||||
break;
|
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) {
|
if (!placed) {
|
||||||
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] No slot for boss!");
|
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] No slot for monster! uuid:", mon.uuid);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 普通怪填充剩余空位
|
|
||||||
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!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
* 设计说明:
|
* 设计说明:
|
||||||
* - 战场固定 5 个占位槽(索引 0-4)。
|
* - 战场固定 5 个占位槽(索引 0-4)。
|
||||||
* - Boss 占 2 个槽位,只能放在 0、2 号位(确保有连续 2 格)。
|
* - Boss 默认占 3 个槽位,只要有连续 3 格空闲即可放置。
|
||||||
* - MissionMonComp 在每波开始时读取本配置,决定刷怪组合。
|
* - MissionMonComp 在每波开始时读取本配置,决定刷怪组合。
|
||||||
*
|
*
|
||||||
* 注意:
|
* 注意:
|
||||||
@@ -77,7 +77,7 @@ export const MonList = {
|
|||||||
[MonType.Melee]: [6001,6002,6003], // 近战怪池
|
[MonType.Melee]: [6001,6002,6003], // 近战怪池
|
||||||
[MonType.Long]: [6004,6005], // 远程怪池
|
[MonType.Long]: [6004,6005], // 远程怪池
|
||||||
[MonType.Support]: [6005], // 辅助怪池
|
[MonType.Support]: [6005], // 辅助怪池
|
||||||
[MonType.MeleeBoss]:[6006,6015], // 近战 Boss 池
|
[MonType.MeleeBoss]:[6006,6105], // 近战 Boss 池
|
||||||
[MonType.LongBoss]:[6104], // 远程 Boss 池
|
[MonType.LongBoss]:[6104], // 远程 Boss 池
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,38 +109,26 @@ export interface IWaveSlot {
|
|||||||
// - type: 怪物类型 (参考 MonType,如近战 0,远程 1,Boss 3 等)。
|
// - type: 怪物类型 (参考 MonType,如近战 0,远程 1,Boss 3 等)。
|
||||||
// - count: 该类型的怪在场上同时存在几个。
|
// - count: 该类型的怪在场上同时存在几个。
|
||||||
// - slotsPerMon: (可选) 单个怪物体积占用几个占位坑,默认为 1。
|
// - slotsPerMon: (可选) 单个怪物体积占用几个占位坑,默认为 1。
|
||||||
// 大型 Boss 设为 2,它会跨占位降落。
|
// 大型 Boss 默认设为 3,它会跨占位降落。
|
||||||
//
|
//
|
||||||
// 【规则约束】:
|
// 【规则约束】:
|
||||||
// - 全场固定 5 个槽位(索引 0-4)。
|
// - 全场固定 5 个槽位(索引 0-4)。
|
||||||
// - Boss 固定占用 2 个位置,且只能出现在 1、3 号位(对应索引 0, 2)。
|
// - Boss 默认占用 3 个位置,只要有连续 3 格即可。
|
||||||
// - 每波怪物总槽位占用不能超过 5。
|
// - 每波怪物总槽位占用不能超过 5。
|
||||||
// =========================================================================================
|
// =========================================================================================
|
||||||
|
|
||||||
/** 各波次的怪物占位配置(key = 波次编号) */
|
/** 各波次的怪物占位配置(key = 波次编号) */
|
||||||
export const WaveSlotConfig: { [wave: number]: IWaveSlot[] } = {
|
export const WaveSlotConfig: { [wave: number]: IWaveSlot[] } = {
|
||||||
/** 第 1 波:2 近战 + 3 远程 */
|
|
||||||
|
/** 第 1 波:2 近战 + 1 近战Boss(默认占3格) */
|
||||||
1: [
|
1: [
|
||||||
{ type: MonType.Melee, count: 2 },
|
{ type: MonType.Melee, count: 2 },
|
||||||
{ type: MonType.Long, count: 3 }
|
{ type: MonType.MeleeBoss, count: 1 }
|
||||||
],
|
],
|
||||||
/** 第 2 波:2 近战 + 2 远程 + 1 辅助 */
|
/** 第 2波:2 近战 + 1 远程Boss(默认占3格) */
|
||||||
2: [
|
2: [
|
||||||
{ type: MonType.Melee, count: 2 },
|
{ type: MonType.Melee, count: 2 },
|
||||||
{ type: MonType.Long, count: 2 },
|
{ type: MonType.LongBoss, count: 1 }
|
||||||
{ type: MonType.Support, count: 1 }
|
|
||||||
],
|
|
||||||
/** 第 3 波:2 近战 + 1 近战Boss(占2格) + 1 远程 */
|
|
||||||
3: [
|
|
||||||
{ type: MonType.Melee, count: 2 },
|
|
||||||
{ type: MonType.MeleeBoss, count: 1, slotsPerMon: 2 },
|
|
||||||
{ type: MonType.Long, count: 1 }
|
|
||||||
],
|
|
||||||
/** 第 4 波:2 近战 + 1 远程 + 1 远程Boss(占2格) */
|
|
||||||
4: [
|
|
||||||
{ type: MonType.Melee, count: 2 },
|
|
||||||
{ type: MonType.Long, count: 1 },
|
|
||||||
{ type: MonType.LongBoss, count: 1, slotsPerMon: 2 }
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user