feat(map): 新增分段刷怪机制并调整教程波次怪物数量

1.  调整教程专用蓝图模板的近战怪生成数量从5个改为2个,并同步更新文档说明
2.  为刷怪组件添加三段式分段刷怪逻辑,每阶段间添加延迟,优化刷怪节奏
3.  重构波次初始化逻辑,提取为setupWaveData方法减少重复代码
This commit is contained in:
walkpan
2026-05-15 23:23:19 +08:00
parent f515feda7b
commit 437982c06f
4 changed files with 192 additions and 109 deletions

View File

@@ -98,6 +98,14 @@ export class MissionMonCompComp extends CCComp {
private pendingMonsters: GeneratedMonster[] = [];
/** 增量刷怪计时器 */
private spawnTimer: number = 0;
/** 分段刷怪阶段 (1, 2, 3) */
private currentSpawnPhase: number = 1;
/** 下一阶段刷怪的延迟计时器 */
private phaseDelayTimer: number = 0;
/** 当前阶段目标生成的怪物总数 */
private phaseTargetCount: number = 0;
/** 当前阶段已生成的怪物数 */
private phaseSpawnedCount: number = 0;
// ======================== 生命周期 ========================
@@ -126,21 +134,35 @@ export class MissionMonCompComp extends CCComp {
if(smc.mission.stop_spawn_mon) return;
// 逐步刷怪逻辑
// 逐步刷怪逻辑 (分 3 段刷出)
if (this.pendingMonsters.length > 0) {
// 如果当前阶段的怪物已经刷完,则进入延迟等待下一阶段
if (this.phaseSpawnedCount >= this.phaseTargetCount && this.currentSpawnPhase < 3) {
this.phaseDelayTimer -= dt;
if (this.phaseDelayTimer <= 0) {
this.currentSpawnPhase++;
this.phaseSpawnedCount = 0;
this.phaseTargetCount = this.currentSpawnPhase === 3 ?
this.pendingMonsters.length :
Math.ceil(this.pendingMonsters.length / (4 - this.currentSpawnPhase));
}
return;
}
this.spawnTimer += dt;
// 控制刷怪速率:例如每 0.2 秒刷 1-2 只
if (this.spawnTimer > 0.2) {
this.spawnTimer = 0;
// 一次出 2 只,加快进度
for (let i = 0; i < 2; i++) {
if (this.pendingMonsters.length === 0) break;
if (this.pendingMonsters.length === 0 || this.phaseSpawnedCount >= this.phaseTargetCount) break;
const monData = this.pendingMonsters.shift()!;
const lane = this.pickBalancedLane();
console.log(`[MissionMonComp] 准备生成怪物 UUID=${monData.uuid}, 剩余数量=${this.pendingMonsters.length}`);
console.log(`[MissionMonComp] [Phase ${this.currentSpawnPhase}] 准备生成怪物 UUID=${monData.uuid}, 剩余数量=${this.pendingMonsters.length}`);
this.addMonsterAt(lane, this.laneIndices[lane], monData);
this.laneIndices[lane]++;
this.waveSpawnedCount++;
this.phaseSpawnedCount++;
}
}
}
@@ -168,6 +190,32 @@ export class MissionMonCompComp extends CCComp {
start() {
}
private setupWaveData(monsters: GeneratedMonster[]) {
this.pendingMonsters = monsters;
smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length;
this.waveTargetCount = monsters.length;
// 初始化分段刷怪状态
this.currentSpawnPhase = 1;
this.phaseSpawnedCount = 0;
// 第一段生成 1/3 的怪物
this.phaseTargetCount = Math.ceil(this.pendingMonsters.length / 3);
// 每段之间的延迟时间,可以根据需要调整,例如 3 秒
this.phaseDelayTimer = 3.0;
let hasBoss = monsters.some(m => m.isBoss);
console.log(`[MissionMonComp] 波次 ${this.currentWave} 生成怪物总数: ${this.waveTargetCount}`);
const uuids = monsters.map(m => m.uuid);
console.log(`[MissionMonComp] 波次 ${this.currentWave} 怪物 UUID 列表:`, uuids);
oops.message.dispatchEvent(GameEvent.NewWave, {
wave: this.currentWave,
total: this.waveTargetCount,
bossWave: hasBoss,
});
}
/**
* 战斗准备:重置所有运行时状态并开始第一波。
*/
@@ -186,20 +234,7 @@ export class MissionMonCompComp extends CCComp {
// 预生成第一波数据以获取数量和 Boss 信息
const monsters = spawningEngine.generateWave(this.currentWave);
this.pendingMonsters = monsters;
smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length;
this.waveTargetCount = monsters.length;
let hasBoss = monsters.some(m => m.isBoss);
console.log(`[MissionMonComp] 波次 ${this.currentWave} 生成怪物总数: ${this.waveTargetCount}`);
const uuids = monsters.map(m => m.uuid);
console.log(`[MissionMonComp] 波次 ${this.currentWave} 怪物 UUID 列表:`, uuids);
oops.message.dispatchEvent(GameEvent.NewWave, {
wave: this.currentWave,
total: this.waveTargetCount,
bossWave: hasBoss,
});
this.setupWaveData(monsters);
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System");
}
@@ -265,20 +300,7 @@ export class MissionMonCompComp extends CCComp {
// 预生成新一波数据以获取数量和 Boss 信息
const monsters = spawningEngine.generateWave(this.currentWave);
this.pendingMonsters = monsters;
smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length;
this.waveTargetCount = monsters.length;
let hasBoss = monsters.some(m => m.isBoss);
console.log(`[MissionMonComp] 波次 ${this.currentWave} 生成怪物总数: ${this.waveTargetCount}`);
const uuids = monsters.map(m => m.uuid);
console.log(`[MissionMonComp] 波次 ${this.currentWave} 怪物 UUID 列表:`, uuids);
oops.message.dispatchEvent(GameEvent.NewWave, {
wave: this.currentWave,
total: this.waveTargetCount,
bossWave: hasBoss,
});
this.setupWaveData(monsters);
}
private onPhasePrepareEnd() {

View File

@@ -464,7 +464,7 @@ export const BlueprintTemplates: BlueprintTemplate[] = [
// ---- 教程专用 ----
{ id: "TUTORIAL", type: TemplateType.NORMAL, tierMin: 1, allowAffix: false,
slots: [{ typePool: [MonType.Melee], countMin: 5, countMax: 5, weight: 1.0 }] },
slots: [{ typePool: [MonType.Melee], countMin: 2, countMax: 2, weight: 1.0 }] },
]
// ======================== 自适应难度配置 ========================

View File

@@ -442,7 +442,7 @@ delta calculation per wave:
### 核心波次生成
- **GIVEN** 游戏开始adaptive_factor=1.0**WHEN** 进入 W1**THEN** 生成 5 个 Melee 怪T1, 无词缀HP=120, AP=12。
- **GIVEN** 游戏开始adaptive_factor=1.0**WHEN** 进入 W1**THEN** 生成 2 个 Melee 怪T1, 无词缀HP=120, AP=12。
- **GIVEN** 当前 Tier 1 W2攀升波**WHEN** 模板选取完成,**THEN** 模板类型为 NORMAL 或 MIXED且模板的怪物槽位池中包含 Long 类型cost=40。运行 100 次抽取Long 类型出现在槽位池中的比例为 100%。
- **GIVEN** W6Boss 波),**WHEN** 模板选取完成,**THEN** 模板类型为 BOSSmandatory_slots 包含 Boss 类型,至少生成 1 个 MeleeBoss + 10-15 个普通怪。
- **GIVEN** Tier 2 W1REST 波),**WHEN** 模板选取完成,**THEN** 模板类型为 RESTtemplate_modifier=0.5x。