feat(肉鸽): 重构为基于波次的刷怪系统

- 废弃动态威胁预算算法,改用确定性的15波配置(每分钟1波)
- 引入三阶段节奏设计:构筑期、磨合期、极限期,每波有独立怪物权重池
- 简化刷怪逻辑,移除复杂的预算计算和英雄血量响应机制
- 特殊事件怪物改为队列处理,与波次系统并行运行
- 优化代码结构,移除冗余状态变量和未使用的方法
This commit is contained in:
walkpan
2026-01-31 11:27:04 +08:00
parent 26b463048f
commit 1b1102c542
2 changed files with 267 additions and 454 deletions

View File

@@ -5,8 +5,8 @@ import { Monster } from "../hero/Mon";
import { MonStart } from "../common/config/heroSet";
import { smc } from "../common/SingletonModuleComp";
import { GameEvent } from "../common/config/GameEvent";
// 导入肉鸽配置
import { getStageMonConfigs, MonType, generateMonstersFromBudget, getRogueConfig } from "./RogueConfig";
// 导入新的肉鸽配置
import { getCurrentWave, MonType, WaveConfig } from "./RogueConfig";
import { BuffConf } from "../common/config/SkillSet";
import { IndexSet, FacSet, BoxSet } from "../common/config/GameSet";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
@@ -17,7 +17,7 @@ const { ccclass, property } = _decorator;
@ccclass('MissionMonCompComp')
@ecs.register('MissionMonComp', false)
export class MissionMonCompComp extends CCComp {
// 添加刷怪队列 - 使用新的RogueConfig格式
// 刷怪队列 (主要用于特殊事件插队)
private MonQueue: Array<{
uuid: number,
position: number,
@@ -25,20 +25,20 @@ export class MissionMonCompComp extends CCComp {
level: number,
buffs: BuffConf[]
}> = [];
private isSpawning: boolean = false;// 是否正在生成怪物
private spawnInterval: number = 0.6; // 每个怪物生成间隔时间(减半速度)
private spawnTimer: number = 0; // 生成计时器
private spawnCount: number = 0; // 召唤计数器
// private pauseInterval: number = 5.0; // 暂停间隔时间5秒
// private isPausing: boolean = false; // 是否正在暂停
private eventProcessed: boolean = false; // 事件是否已处理
/** 全局生成顺序计数器,用于层级管理 */
private globalSpawnOrder: number = 0;
/** 游戏进行时间(秒) */
private gameTime: number = 0;
/** 刷怪逻辑计时器(每秒执行一次) */
private spawnLogicTimer: number = 0;
/** 波次刷怪计时器 */
private waveTimer: number = 0;
/** 队列处理计时器 */
private queueTimer: number = 0;
onLoad(){
this.on(GameEvent.FightReady,this.fight_ready,this)
@@ -56,8 +56,8 @@ export class MissionMonCompComp extends CCComp {
if (!args) return;
console.log(`[MissionMonComp] 收到特殊刷怪指令:`, args);
// 插入队列头部,优先生成
this.MonQueue.unshift({
// 插入队列
this.MonQueue.push({
uuid: args.uuid,
position: args.position !== undefined ? args.position : 2, // 默认中间
type: args.type,
@@ -65,27 +65,24 @@ export class MissionMonCompComp extends CCComp {
buffs: args.buffs || []
});
// 让刷怪计时器立即满足条件,以便尽快生成
// 注意:不直接调用 spawnNextMonster 是为了保持 update 循环的一致性
const config = getRogueConfig();
this.spawnTimer = config.spawnInterval + 0.1;
// 立即触发一次队列检查 (可选,让 update 尽快处理)
this.queueTimer = 1.0;
}
/** 视图层逻辑代码分离演示 */
start() {
// var entity = this.ent as ecs.Entity; // ecs.Entity 可转为当前模块的具体实体对象
// this.on(ModuleEvent.Cmd, this.onHandler, this);
}
fight_ready(){
// console.log("[MissionMonComp]:fight_ready")
smc.vmdata.mission_data.mon_num=0
// 重置生成顺序计数器
this.globalSpawnOrder = 0
this.gameTime = 0
this.spawnLogicTimer = 0
this.waveTimer = 0
this.queueTimer = 0
this.MonQueue = []
this.do_mon_wave()
this.spawnCount = 0
console.log("[MissionMonComp] Starting Wave System (15-min Cycle)");
}
protected update(dt: number): void {
@@ -96,137 +93,75 @@ export class MissionMonCompComp extends CCComp {
// 累加游戏时间
this.gameTime += dt;
const config = getRogueConfig();
// ==========================================
// 新增:每秒执行一次刷怪逻辑 (Threat Budget)
// ==========================================
this.spawnLogicTimer += dt;
if (this.spawnLogicTimer >= config.spawnLogicInterval) {
this.spawnLogicTimer = 0;
// 检查最大怪物数量限制
if (smc.vmdata.mission_data.mon_num < config.maxMonsterCount) {
// 获取英雄血量比例
const hpRatio = this.getHeroHpRatio();
// 生成怪物
const newMonsters = generateMonstersFromBudget(this.gameTime, hpRatio);
// 添加到队列
newMonsters.forEach(mon => {
this.addToStageSpawnQueue(
mon.uuid,
mon.position !== undefined ? mon.position : 0,
mon.type,
mon.level,
mon.buffs || []
);
});
// 获取当前波次配置
const currentWave = getCurrentWave(this.gameTime);
// 1. 优先处理特殊怪队列
if (this.MonQueue.length > 0) {
this.queueTimer += dt;
// 队列出怪速度快于普通波次 (0.5秒一只)
if (this.queueTimer >= 0.5) {
this.spawnNextFromQueue();
this.queueTimer = 0;
}
}
// 处理随机事件
// 处理刷怪队列
if (this.MonQueue.length > 0 && !this.isSpawning) {
this.spawnTimer += dt;
// 2. 处理波次自然刷怪
this.waveTimer += dt;
if (this.waveTimer >= currentWave.spawnInterval) {
this.waveTimer = 0;
// 正常召唤间隔
if (this.spawnTimer >= config.spawnInterval) {
this.spawnNextMonster();
this.spawnTimer = 0;
// 检查同屏数量限制
if (smc.vmdata.mission_data.mon_num < currentWave.maxActive) {
this.spawnWaveMonster(currentWave);
}
}
}
do_mon_wave(){
// 重置召唤相关状态
this.spawnCount = 0;
// this.isPausing = false;
this.spawnTimer = 0;
this.eventProcessed = false;
// const cStage = smc.data.mission;
// // 使用新的肉鸽关卡配置
// let level=smc.vmdata.mission_data.level
// const monsConf = getStageMonConfigs(cStage);
// // console.log(`[MissionMonComp]:第${cStage}关 - ${stageType}类型,怪物数量: ${monsConf.length}`);
// const monsConfFiltered = monsConf.filter((mon: any, index) => index === 0);
// this.generateMonsters(monsConfFiltered);
console.log("[MissionMonComp] Starting Threat Budget Wave System");
}
/**
* 获取英雄血量比例
* 从当前波次配置生成并生成怪物
*/
private getHeroHpRatio(): number {
// 查询带有 HeroAttrsComp 的实体
// 注意:这里假设只有一个英雄,且性能允许每秒查询一次
const entities = ecs.query(ecs.allOf(HeroAttrsComp));
for (const e of entities) {
const attrs = e.get(HeroAttrsComp);
if (attrs && attrs.fac === FacSet.HERO) {
const maxHp = attrs.Attrs[Attrs.HP_MAX] || 1;
return attrs.hp / maxHp;
private spawnWaveMonster(wave: WaveConfig) {
if (!wave.weights || wave.weights.length === 0) return;
// 权重随机算法
const totalWeight = wave.weights.reduce((sum, item) => sum + item.weight, 0);
let random = Math.random() * totalWeight;
let selectedUuid = wave.weights[0].uuid;
let selectedType = wave.weights[0].type || MonType.NORMAL;
for (const item of wave.weights) {
random -= item.weight;
if (random <= 0) {
selectedUuid = item.uuid;
selectedType = item.type || MonType.NORMAL;
break;
}
}
return 1.0; // 默认满血
// 随机位置 (0-4)
const position = Math.floor(Math.random() * 5);
// 等级随时间增长 (每分钟+1级)
const level = Math.floor(this.gameTime / 60) + 1;
this.addMonster(
selectedUuid,
position,
selectedType,
level,
[],
this.gameTime
);
this.spawnCount++;
}
// 根据新的关卡配置生成怪物
private generateMonsters(monsConf: any[]) {
const cStage = smc.data.mission;
// 设置怪物总数
// console.log("[MissionMonComp] generateMonsters",monsConf)
if (!monsConf || monsConf.length === 0) {
console.warn(`[MissionMonComp]:关卡${cStage}配置中没有怪物信息`);
return;
}
// 为每个怪物配置生成怪物
monsConf.forEach((mon: any, index: number) => {
const { uuid, type, level, buffs, position } = mon;
// 使用配置中的位置,如果没有则使用索引
const spawnPosition = position !== undefined ? position : (index % 5);
this.addToStageSpawnQueue(
uuid,
spawnPosition,
type,
level,
buffs
);
});
// console.log(`[MissionMonComp]:关卡${cStage}将生成 ${monsConf.length} 只怪物`);
}
// 添加到关卡刷怪队列 - 使用新的配置格式
private addToStageSpawnQueue(
uuid: number,
position: number,
type: MonType = MonType.NORMAL,
level: number = 1,
buffs: BuffConf[] = []
) {
this.MonQueue.push({
uuid: uuid,
position: position,
type: type,
level: level,
buffs: buffs
});
}
// 从队列中生成下一个怪物 - 使用新的配置格式
private spawnNextMonster() {
/**
* 从队列中生成下一个怪物
*/
private spawnNextFromQueue() {
if (this.MonQueue.length === 0) return;
const monsterData = this.MonQueue.shift();
@@ -239,9 +174,7 @@ export class MissionMonCompComp extends CCComp {
monsterData.buffs,
this.gameTime
);
// 增加召唤计数
this.spawnCount++;
// console.log(`[MissionMonComp]: 召唤第${this.spawnCount}只${monsterData.type}怪物,剩余队列: ${this.MonQueue.length}`);
}
}
@@ -255,46 +188,22 @@ export class MissionMonCompComp extends CCComp {
) {
let mon = ecs.getEntity<Monster>(Monster);
let scale = -1;
// 使用 MonStart 计算怪物出生位置:
// x 从 START_X 开始,按 START_I 的间隔递增;
// y 在线路之间交替0->SLINE_1, 1->SLINE_2, 2->SLINE_3, 3->SLINE_4
const x = MonStart.START_X + Math.floor(i / 4) * MonStart.START_I;
const x = MonStart.START_X + Math.floor(i / 4) * MonStart.START_I;
let y = BoxSet.GAME_LINE;
let lane = 0;
// switch (i % 4) {
// case 0:
// y = MonStart.SLINE_1;
// lane = 0;
// break;
// case 1:
// y = MonStart.SLINE_2;
// lane = 1;
// break;
// case 2:
// y = MonStart.SLINE_3;
// lane = 2;
// break;
// case 3:
// y = MonStart.SLINE_4;
// lane = 3;
// break;
// }
let pos: Vec3 = v3(x, y, 0);
// 递增全局生成顺序 - 🔥 添加溢出保护
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999; // 防止无限增长在999处循环重置
// 递增全局生成顺序 - 溢出保护
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999;
// 生成怪物,传递线路和生成顺序
// 生成怪物
mon.load(pos, scale, uuid, lv, monType, buffs, false, lane, this.globalSpawnOrder, gameTime);
}
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
reset() {
// this.node.destroy();
}
}
}