refactor(怪物生成): 重构波次配置与生成逻辑

- 将波次配置从属性迁移至配置文件,增强可维护性
- 重构怪物生成逻辑,使用基于槽位的排队机制
- 移除旧的计时生成方式,改为配置驱动
This commit is contained in:
panw
2026-04-03 16:52:12 +08:00
parent 1817c14b25
commit 0129771435
2 changed files with 66 additions and 89 deletions

View File

@@ -23,14 +23,6 @@ export class MissionMonCompComp extends CCComp {
private static readonly MON_DROP_HEIGHT = 280;
@property({ tooltip: "是否启用调试日志" })
private debugMode: boolean = false;
@property({ tooltip: "每波基础普通怪数量" })
private baseMonstersPerWave: number = 5;
@property({ tooltip: "每波额外增加普通怪数量" })
private waveMonsterGrowth: number = 0;
@property({ tooltip: "多少波刷新一次 Boss" })
private bossWaveInterval: number = 5;
@property({ tooltip: "同一波内刷怪间隔(秒)" })
private waveSpawnCd: number = 0.35;
// 刷怪队列(用于插队生成:比如运营活动怪、技能召唤怪、剧情强制怪)
// 约定:队列里的怪会优先于常规刷新处理
@@ -53,11 +45,9 @@ export class MissionMonCompComp extends CCComp {
private globalSpawnOrder: number = 0;
/** 插队刷怪处理计时器 */
private queueTimer: number = 0;
private waveSpawnTimer: number = 0;
private currentWave: number = 0;
private waveTargetCount: number = 0;
private waveSpawnedCount: number = 0;
private bossSpawnedInWave: boolean = false;
onLoad(){
this.on(GameEvent.FightReady,this.fight_ready,this)
this.on("SpawnSpecialMonster", this.onSpawnSpecialMonster, this);
@@ -87,11 +77,9 @@ export class MissionMonCompComp extends CCComp {
smc.mission.stop_spawn_mon = false
this.globalSpawnOrder = 0
this.queueTimer = 0
this.waveSpawnTimer = 0
this.currentWave = 0
this.waveTargetCount = 0
this.waveSpawnedCount = 0
this.bossSpawnedInWave = false
this.MonQueue = []
this.startNextWave()
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System");
@@ -108,7 +96,6 @@ export class MissionMonCompComp extends CCComp {
if(!smc.mission.in_fight) return;
if(smc.mission.stop_spawn_mon) return;
this.updateSpecialQueue(dt);
this.updateWaveSpawn(dt);
}
private updateSpecialQueue(dt: number) {
@@ -125,82 +112,50 @@ export class MissionMonCompComp extends CCComp {
this.enqueueMonsterRequest(item.uuid, isBoss, upType, Math.max(1, Number(item.level ?? 1)), slotsPerMon, true);
}
private updateWaveSpawn(dt: number) {
this.waveSpawnTimer += dt;
if (this.waveSpawnTimer < this.waveSpawnCd) return;
this.waveSpawnTimer = 0;
if (this.waveSpawnedCount >= this.waveTargetCount) {
if (this.isBossWave() && !this.bossSpawnedInWave) {
const bossUuid = this.getRandomBossUuid();
const bossUpType = this.getRandomUpType();
this.enqueueMonsterRequest(bossUuid, true, bossUpType, 1, 2);
this.bossSpawnedInWave = true;
}
return;
}
const uuid = this.getRandomNormalMonsterUuid();
const upType = this.getRandomUpType();
this.enqueueMonsterRequest(uuid, false, upType, 1, 1);
this.waveSpawnedCount += 1;
}
private startNextWave() {
this.currentWave += 1;
smc.vmdata.mission_data.level = this.currentWave;
this.waveTargetCount = Math.max(1, this.baseMonstersPerWave + (this.currentWave - 1) * this.waveMonsterGrowth);
this.waveSpawnedCount = 0;
this.bossSpawnedInWave = false;
this.waveSpawnTimer = this.waveSpawnCd;
this.resetSlotSpawnData(this.currentWave);
this.primeWaveInitialBurst();
let hasBoss = false;
const config = WaveSlotConfig[this.currentWave] || DefaultWaveSlot;
for (const slot of config) {
if (slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss) {
hasBoss = true;
}
}
oops.message.dispatchEvent(GameEvent.NewWave, {
wave: this.currentWave,
total: this.waveTargetCount,
bossWave: this.isBossWave(),
bossWave: hasBoss,
});
}
private tryAdvanceWave() {
if (this.waveSpawnedCount < this.waveTargetCount) return;
if (this.isBossWave() && !this.bossSpawnedInWave) return;
if (this.hasPendingSlotQueue()) return;
if (this.hasActiveSlotMonster()) return;
if (smc.vmdata.mission_data.mon_num > 0) return;
this.startNextWave();
}
private isBossWave() {
const interval = Math.max(1, Math.floor(this.bossWaveInterval));
return this.currentWave > 0 && this.currentWave % interval === 0;
}
private getCurrentStage(): number {
return Math.max(0, this.currentWave - 1);
}
private getRandomUpType(): UpType {
// 从 StageGrow 的 key 中采样,保证新增配置无需改逻辑
const keys = Object.keys(StageGrow).map(v => Number(v) as UpType);
const index = Math.floor(Math.random() * keys.length);
return keys[index] ?? UpType.AP1_HP1;
}
private getRandomNormalMonsterUuid(): number {
// MonType 是常量对象,这里通过值采样拿到怪物类型 id
const typeKeys = Object.keys(MonType).map(k => (MonType as any)[k]).filter(v => typeof v === "number");
const randomType = typeKeys[Math.floor(Math.random() * typeKeys.length)] as number;
// 如果某类型配置被清空,回退到 AP 类型,避免空池异常
const pool = MonList[randomType] || MonList[MonType.Melee];
private getRandomUuidByType(monType: number): number {
const pool = (MonList as any)[monType] || MonList[MonType.Melee];
if (!pool || pool.length === 0) return 6001;
const index = Math.floor(Math.random() * pool.length);
return pool[index];
}
private getRandomBossUuid(): number {
// 目前 Boss 池可扩展为多个,先走随机抽取
const index = Math.floor(Math.random() * BossList.length);
return BossList[index];
}
private resolveGrowPair(upType: UpType, isBoss: boolean): [number, number] {
// 普通怪基础成长StageGrow
const grow = StageGrow[upType] || StageGrow[UpType.AP1_HP1];
@@ -214,27 +169,21 @@ export class MissionMonCompComp extends CCComp {
return SpawnPowerBias;
}
private primeWaveInitialBurst() {
const remain = this.waveTargetCount - this.waveSpawnedCount;
if (remain <= 0) return;
const burstCount = Math.min(this.slotSpawnQueues.length, remain);
for (let i = 0; i < burstCount; i++) {
const uuid = this.getRandomNormalMonsterUuid();
const upType = this.getRandomUpType();
this.enqueueMonsterRequest(uuid, false, upType, 1, 1);
}
this.waveSpawnedCount += burstCount;
}
private resetSlotSpawnData(wave: number = 1) {
const config: IWaveSlot[] = WaveSlotConfig[wave] || DefaultWaveSlot;
let totalSlots = 0;
let totalMonsters = 0;
for (const slot of config) {
const slotsPerMon = slot.slotsPerMon || 1;
const monCount = slot.monCount || 1;
totalSlots += slot.count * slotsPerMon;
totalMonsters += slot.count * monCount;
}
this.waveTargetCount = totalMonsters;
this.waveSpawnedCount = 0;
this.slotSpawnQueues = Array.from(
{ length: totalSlots },
() => []
@@ -243,18 +192,31 @@ export class MissionMonCompComp extends CCComp {
{ length: totalSlots },
() => null
);
this.confirmWaveSlotTypes(config);
}
private confirmWaveSlotTypes(config: IWaveSlot[]) {
this.slotRangeTypes = [];
this.slotSizes = [];
let slotIndex = 0;
for (const slot of config) {
const slotsPerMon = slot.slotsPerMon || 1;
const monCount = slot.monCount || 1;
const isBoss = slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss;
for (let i = 0; i < slot.count; i++) {
const currentSlotIndex = slotIndex;
// 设置槽位类型和大小
for (let s = 0; s < slotsPerMon; s++) {
this.slotRangeTypes.push(slot.type);
this.slotSizes.push(slotsPerMon);
slotIndex++;
}
// 根据配置数量,直接在波次开始时把该坑位要刷的所有怪排入队列
for (let m = 0; m < monCount; m++) {
const uuid = this.getRandomUuidByType(slot.type);
const upType = this.getRandomUpType();
const request = { uuid, isBoss, upType, monLv: wave, slotsPerMon };
this.slotSpawnQueues[currentSlotIndex].push(request);
}
}
}