refactor(monster): 重构波次怪物管理逻辑,优化配置与流程

1.  更新怪物配置注释,修正近战/远程怪物的描述与分类
2.  移除MissionComp中过时的自适应刷怪逻辑
3.  重构MissionMonComp:删除插队刷怪队列、简化波次流程、统一怪物生成逻辑
4.  移除冗余日志与注释,优化代码可读性
5.  调整波次准备阶段的怪物生成时机与数据处理
This commit is contained in:
pan
2026-06-23 17:05:07 +08:00
parent 46fa481607
commit 6c2f1defa9
4 changed files with 175 additions and 1219 deletions

View File

@@ -381,40 +381,40 @@ export const HeroInfo: Record<number, heroInfo> = {
*/ */
// 基础怪物 (全部远程攻击HType仅决定站位) // 基础怪物 (全部固定点位站桩攻击HType仅决定是前排还是后排)
// 近战位怪物 (站在前排,承受更多伤害) — v5: TD节奏CD多而弱爽感设计 // 前排怪物 (站在前排,承受更多伤害) — v5: TD节奏CD多而弱爽感设计
6001:{uuid:6001,name:"兽人战士",path:"m1", fac:FacSet.MON,lv:1,type:HType.Melee,hp:220,ap:10,speed:70, 6001:{uuid:6001,name:"兽人战士",path:"m1", fac:FacSet.MON,lv:1,type:HType.Melee,hp:220,ap:10,speed:70,
skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"基础近战位怪"}, skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"基础前排怪"},
6002:{uuid:6002,name:"兽人精锐战士",path:"m2", fac:FacSet.MON,lv:1,type:HType.Melee,hp:300,ap:14,speed:110, 6002:{uuid:6002,name:"兽人精锐战士",path:"m2", fac:FacSet.MON,lv:1,type:HType.Melee,hp:300,ap:14,speed:110,
skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"进阶近战位怪,更快更痛"}, skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"进阶前排怪,更快更痛"},
6003:{uuid:6003,name:"兽人重装兵",path:"m3", fac:FacSet.MON,lv:1,type:HType.Melee,hp:850,ap:20,speed:50, 6003:{uuid:6003,name:"兽人重装兵",path:"m3", fac:FacSet.MON,lv:1,type:HType.Melee,hp:850,ap:20,speed:50,
skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"重型坦克怪高HP慢攻"}, skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"重型坦克怪高HP慢攻"},
// 远程位怪物 (站在后排,输出更高) // 后排怪物 (站在后排,输出更高)
6004:{uuid:6004,name:"兽人射手",path:"m4", fac:FacSet.MON,lv:1,type:HType.Long,hp:190,ap:35,speed:70, 6004:{uuid:6004,name:"兽人射手",path:"m4", fac:FacSet.MON,lv:1,type:HType.Long,hp:190,ap:35,speed:70,
skills:{6008:{uuid:6008,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"远程高DPS怪"}, skills:{6008:{uuid:6008,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"后排高DPS怪"},
6005:{uuid:6005,name:"兽人刺客",path:"m5", fac:FacSet.MON,lv:1,type:HType.Long,hp:210,ap:38,speed:130, 6005:{uuid:6005,name:"兽人刺客",path:"m5", fac:FacSet.MON,lv:1,type:HType.Long,hp:210,ap:38,speed:130,
skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"高AP快速攻击刺客"}, skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"高AP快速攻击刺客"},
// 特殊位怪物 // 特殊位怪物
6006:{uuid:6006,name:"骷髅领主",path:"m6", fac:FacSet.MON,lv:1,type:HType.Melee,hp:5000,ap:20,speed:60, 6006:{uuid:6006,name:"骷髅领主",path:"m6", fac:FacSet.MON,lv:1,type:HType.Melee,hp:5000,ap:20,speed:60,
skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"MiniBoss级坦克"}, skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"前排MiniBoss级坦克"},
6007:{uuid:6007,name:"兽人术士",path:"m7", fac:FacSet.MON,lv:1,type:HType.Melee,hp:300,ap:24,speed:70, 6007:{uuid:6007,name:"兽人术士",path:"m7", fac:FacSet.MON,lv:1,type:HType.Long,hp:300,ap:24,speed:70,
skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"法师怪,远程魔法攻击"}, skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"后排法师怪,魔法攻击"},
6008:{uuid:6008,name:"兽人火法",path:"m8", fac:FacSet.MON,lv:1,type:HType.Melee,hp:270,ap:32,speed:70, 6008:{uuid:6008,name:"兽人火法",path:"m8", fac:FacSet.MON,lv:1,type:HType.Long,hp:270,ap:32,speed:70,
skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"高输出法师怪"}, skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"后排高输出法师怪"},
// BOSS怪物 — Boss节奏1.2-1.5s删除不存在的6206技能 // BOSS怪物 — Boss节奏1.2-1.5s删除不存在的6206技能
6101:{uuid:6101,name:"兽人首领-双刀战士",path:"mb1", fac:FacSet.MON,lv:6,type:HType.Long,hp:1900,ap:30,speed:120, 6101:{uuid:6101,name:"兽人首领-双刀战士",path:"mb1", fac:FacSet.MON,lv:6,type:HType.Melee,hp:1900,ap:30,speed:120,
skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"远程Boss高攻速"}, skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"前排Boss高攻速"},
6102:{uuid:6102,name:"兽人首领-斧头战士",path:"mb2", fac:FacSet.MON,lv:6,type:HType.Melee,hp:7500,ap:26,speed:60, 6102:{uuid:6102,name:"兽人首领-斧头战士",path:"mb2", fac:FacSet.MON,lv:6,type:HType.Melee,hp:7500,ap:26,speed:60,
skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"近战Boss超高HP"}, skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"前排Boss超高HP"},
6103:{uuid:6103,name:"兽人首领-魔法师",path:"mb3", fac:FacSet.MON,lv:6,type:HType.Long,hp:2250,ap:38,speed:110, 6103:{uuid:6103,name:"兽人首领-魔法师",path:"mb3", fac:FacSet.MON,lv:6,type:HType.Long,hp:2250,ap:38,speed:110,
skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"远程法系Boss高AP"}, skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow2].cd,ccd:0}},info:"后排法系Boss高AP"},
6104:{uuid:6104,name:"兽人首领-射手",path:"mb4", fac:FacSet.MON,lv:6,type:HType.Melee,hp:6800,ap:30,speed:70, 6104:{uuid:6104,name:"兽人首领-射手",path:"mb4", fac:FacSet.MON,lv:6,type:HType.Long,hp:6800,ap:30,speed:70,
skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"近战位Boss均衡型"}, skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"后排位Boss均衡型"},
6105:{uuid:6105,name:"亡灵首领-法师",path:"mb5", fac:FacSet.MON,lv:6,type:HType.Long,hp:2600,ap:42,speed:110, 6105:{uuid:6105,name:"亡灵首领-法师",path:"mb5", fac:FacSet.MON,lv:6,type:HType.Long,hp:2600,ap:42,speed:110,
skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"远程高AP Boss"}, skills:{6103:{uuid:6103,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow1].cd,ccd:0}},info:"后排高AP Boss"},
6106:{uuid:6106,name:"亡灵首领-骑马战士",path:"mb6", fac:FacSet.MON,lv:6,type:HType.Melee,hp:9000,ap:26,speed:130, 6106:{uuid:6106,name:"亡灵首领-骑马战士",path:"mb6", fac:FacSet.MON,lv:6,type:HType.Melee,hp:9000,ap:26,speed:130,
skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"终极Boss最高HP+高速"}, skills:{6005:{uuid:6005,lv:1,cd:AtkSpeedSet[AtkSpeedLv.VerySlow3].cd,ccd:0}},info:"前排终极Boss最高HP+高速"},
}; };

View File

@@ -856,11 +856,6 @@ export class MissionComp extends CCComp {
// 怪物全灭检测:如果战斗阶段场上没有任何活着的怪物,且待刷新的怪物队列也为空,直接结束战斗进入下一波的准备阶段 // 怪物全灭检测:如果战斗阶段场上没有任何活着的怪物,且待刷新的怪物队列也为空,直接结束战斗进入下一波的准备阶段
const pendingCount = smc.vmdata.mission_data.pending_mon_num || 0; const pendingCount = smc.vmdata.mission_data.pending_mon_num || 0;
if (monsterCount === 0 && pendingCount === 0 && smc.mission.play && !smc.mission.pause && this.currentPhase === MissionPhase.Battle) { if (monsterCount === 0 && pendingCount === 0 && smc.mission.play && !smc.mission.pause && this.currentPhase === MissionPhase.Battle) {
let heroesAliveRatio = heroCount / 6.0; // 假设最大 6 个站位,或者直接基于存活数算比例
// 如果能获取当前已部署英雄数最好,这里简化处理,大于 4 个就算高存活
heroesAliveRatio = Math.min(1.0, heroCount / 4.0);
spawningEngine.updateAdaptive(heroesAliveRatio, this.clearTime);
if (this.currentWave >= 15) { if (this.currentWave >= 15) {
// 15 波通关 // 15 波通关
this.open_Victory(null, false); this.open_Victory(null, false);

View File

@@ -3,27 +3,13 @@
* @description 怪物Monster波次刷新管理组件逻辑层 * @description 怪物Monster波次刷新管理组件逻辑层
* *
* 职责: * 职责:
* 1. 管理每一波怪物的 **生成计划**:根据 WaveSlotConfig 生成怪物。 * 1. 管理每一波怪物的生成计划:根据 RogueConfig 生成怪物。
* 2. 处理特殊插队刷怪请求MonQueue优先于常规刷新 * 2. 自动推进波次在准备阶段结束时PhasePrepareEnd统一刷出怪物
* 3. 自动推进波次:当前波所有怪物被清除后自动进入下一波。
* *
* 关键设计: * 关键设计:
* - 突破 5 槽限制,怪物按刷怪顺序依次从 X=280 开始向右(每隔 50排布。 * - 采用 12 个硬编码的网格位置点 (MON_POSITIONS3行x4列)
* - 6 条刷怪线:在三路 Y 轴范围内随机偏移,实现 6 路进军 * - 每次生成最多 12 个怪物,固定在位置点
* - resetSlotSpawnData(wave) 在每波开始时读取配置,分配并立即生成所有怪物 * - 上一波残留怪在波次结束/开始时统一清理
* - 去除跨波 HP 继承,上一波残留怪在波次结束/开始时销毁。
*
* 怪物属性计算公式:
* ap = floor((base_ap + stage × grow_ap) × SpawnPowerBias)
* hp = floor((base_hp + stage × grow_hp) × SpawnPowerBias)
* 其中 stage = currentWave - 1
*
* 依赖:
* - RogueConfig —— 怪物类型、成长值、波次配置
* - Monsterhero/Mon.ts—— 怪物 ECS 实体类
* - HeroInfoheroSet—— 怪物基础属性配置(与英雄共用配置)
* - HeroAttrsComp / MonMoveComp —— 怪物属性和移动组件
* - BoxSet.GAME_LINE —— 地面基准 Y 坐标
*/ */
import { _decorator, v3, Vec3 } from "cc"; import { _decorator, v3, Vec3 } from "cc";
import { mLogger } from "../common/Logger"; import { mLogger } from "../common/Logger";
@@ -31,21 +17,15 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ec
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp"; import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { Monster } from "../hero/Mon"; import { Monster } from "../hero/Mon";
import { HeroInfo, HType } from "../common/config/heroSet";
import { smc } from "../common/SingletonModuleComp"; import { smc } from "../common/SingletonModuleComp";
import { GameEvent } from "../common/config/GameEvent"; import { GameEvent } from "../common/config/GameEvent";
import {BoxSet, FacSet } from "../common/config/GameSet"; import { BoxSet, FacSet } from "../common/config/GameSet";
import { spawningEngine, GeneratedMonster, AffixType, MonType, MonList, TestModeConfig } from "./RogueConfig"; import { spawningEngine, GeneratedMonster, TestModeConfig } from "./RogueConfig";
import { HeroAttrsComp } from "../hero/HeroAttrsComp"; import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { MonMoveComp } from "../hero/MonMoveComp"; import { MonMoveComp } from "../hero/MonMoveComp";
const { ccclass, property } = _decorator; const { ccclass, property } = _decorator;
/**
* MissionMonCompComp —— 怪物波次刷新管理器
*
* 每波开始时根据 WaveSlotConfig 配置生成怪物,
* 战斗中监控数量,所有怪物消灭后自动推进到下一波。
*/
@ccclass('MissionMonCompComp') @ccclass('MissionMonCompComp')
@ecs.register('MissionMonComp', false) @ecs.register('MissionMonComp', false)
export class MissionMonCompComp extends CCComp { export class MissionMonCompComp extends CCComp {
@@ -81,83 +61,32 @@ export class MissionMonCompComp extends CCComp {
@property({ tooltip: "是否启用调试日志" }) @property({ tooltip: "是否启用调试日志" })
private debugMode: boolean = false; private debugMode: boolean = false;
// ======================== 插队刷怪队列 ========================
/**
* 刷怪队列(优先于常规配置处理):
* 用于插队生成(如运营活动怪、技能召唤怪、剧情强制怪)。
*/
private MonQueue: Array<{
/** 怪物 UUID */
uuid: number,
/** 怪物等级 */
level: number,
/** 飞行层 */
flyLane: number,
}> = [];
// ======================== 运行时状态 ======================== // ======================== 运行时状态 ========================
/** 全局生成顺序计数器(用于渲染层级排序) */ /** 全局生成顺序计数器(用于渲染层级排序) */
private globalSpawnOrder: number = 0; private globalSpawnOrder: number = 0;
/** 插队刷怪处理计时器 */
private queueTimer: number = 0;
/** 当前波数 */ /** 当前波数 */
private currentWave: number = 0; private currentWave: number = 0;
/** 当前波的目标怪物总数 */ /** 当前波的目标怪物总数 */
private waveTargetCount: number = 0; private waveTargetCount: number = 0;
/** 当前波已生成的怪物数量 */ /** 当前波已生成的怪物数量 */
private waveSpawnedCount: number = 0; private waveSpawnedCount: number = 0;
/** 等待生成的怪物队列(由新肉鸽引擎提供) */ /** 等待生成的怪物队列 */
private pendingMonsters: GeneratedMonster[] = []; private pendingMonsters: GeneratedMonster[] = [];
// ======================== 生命周期 ======================== // ======================== 生命周期 ========================
onLoad(){ onLoad() {
this.on(GameEvent.FightReady,this.fight_ready,this) this.on(GameEvent.FightReady, this.fight_ready, this);
this.on("SpawnSpecialMonster", this.onSpawnSpecialMonster, this);
this.on("PhasePrepareEnd", this.onPhasePrepareEnd, this); this.on("PhasePrepareEnd", this.onPhasePrepareEnd, this);
this.on("TimeUpAdvanceWave", this.onTimeUpAdvanceWave, this); this.on("TimeUpAdvanceWave", this.onTimeUpAdvanceWave, this);
} }
/**
* 帧更新:
* 1. 检查游戏是否运行中。
* 2. 处理插队刷怪队列。
* 3. 逐步从 pendingMonsters 队列中生成怪物(受 stop_spawn_mon 限制)。
*/
protected update(dt: number): void { protected update(dt: number): void {
smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length; smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length;
if(!smc.mission.play) return
if(smc.mission.pause) return
if(smc.mission.stop_mon_action) return;
if(!smc.mission.in_fight) return;
this.updateSpecialQueue(dt);
} }
// ======================== 事件处理 ======================== start() {}
/**
* 接收特殊刷怪事件并入队。
* @param event 事件名
* @param args { uuid: number, level: number, flyLane?: number }
*/
private onSpawnSpecialMonster(event: string, args: any) {
if (!args) return;
mLogger.log(this.debugMode, 'MissionMonComp', `[MissionMonComp] 收到特殊刷怪指令:`, args);
this.MonQueue.push({
uuid: args.uuid,
level: args.level,
flyLane: args.flyLane || 0
});
// 加速队列消费
this.queueTimer = 1.0;
}
start() {
}
private setupWaveData(monsters: GeneratedMonster[]) { private setupWaveData(monsters: GeneratedMonster[]) {
this.pendingMonsters = monsters.slice(0, MissionMonCompComp.MAX_MONSTERS); this.pendingMonsters = monsters.slice(0, MissionMonCompComp.MAX_MONSTERS);
@@ -166,9 +95,7 @@ export class MissionMonCompComp extends CCComp {
let hasBoss = monsters.some(m => m.isBoss); let hasBoss = monsters.some(m => m.isBoss);
console.log(`[MissionMonComp] 波次 ${this.currentWave} 生成怪物总数: ${this.waveTargetCount}`); mLogger.log(this.debugMode, 'MissionMonComp', `[MissionMonComp] 波次 ${this.currentWave} 生成怪物总数: ${this.waveTargetCount}`);
const uuids = monsters.map(m => m.uuid);
console.log(`[MissionMonComp] 波次 ${this.currentWave} 怪物 UUID 列表:`, uuids);
oops.message.dispatchEvent(GameEvent.NewWave, { oops.message.dispatchEvent(GameEvent.NewWave, {
wave: this.currentWave, wave: this.currentWave,
@@ -180,81 +107,41 @@ export class MissionMonCompComp extends CCComp {
/** /**
* 战斗准备:重置所有运行时状态并开始第一波。 * 战斗准备:重置所有运行时状态并开始第一波。
*/ */
fight_ready(){ fight_ready() {
smc.vmdata.mission_data.mon_num=0 smc.vmdata.mission_data.mon_num = 0;
smc.mission.stop_spawn_mon = false smc.mission.stop_spawn_mon = false;
this.globalSpawnOrder = 0 this.globalSpawnOrder = 0;
this.queueTimer = 0 this.currentWave = 1;
this.currentWave = 1 this.waveTargetCount = 0;
this.waveTargetCount = 0 this.waveSpawnedCount = 0;
this.waveSpawnedCount = 0 this.pendingMonsters = [];
this.MonQueue = []
this.pendingMonsters = []
// 预生成第一波数据以获取数量和 Boss 信息 // 预生成第一波数据以获取数量和 Boss 信息
const monsters = spawningEngine.generateWave(this.currentWave); const monsters = spawningEngine.generateWave(this.currentWave);
this.setupWaveData(monsters); this.setupWaveData(monsters);
// 如果处于测试模式,英雄也需要限制为只产出一个,这部分通知可以配合使用
if (TestModeConfig.enable) { if (TestModeConfig.enable) {
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] 测试模式已开启每波仅生成1只基准怪物"); mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] 测试模式已开启");
} }
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System"); mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System");
} }
// ======================== 插队刷怪 ========================
/**
* 处理插队刷怪队列(每 0.15 秒尝试消费一个):
* 1. 找到后从队列中移除并生成怪物。
*/
private updateSpecialQueue(dt: number) {
if (this.MonQueue.length <= 0) return;
this.queueTimer += dt;
if (this.queueTimer < 0.15) return;
const item = this.MonQueue.shift()!;
this.queueTimer = 0;
const isBoss = MonList[MonType.MeleeBoss].includes(item.uuid) ||
MonList[MonType.LongBoss].includes(item.uuid);
const spawnIndex = this.waveSpawnedCount++;
const targetPosIndex = spawnIndex % MissionMonCompComp.MAX_MONSTERS;
// 构造一个模拟的 GeneratedMonster 数据传递给 addMonsterAtGrid
const base = HeroInfo[item.uuid];
const monData: GeneratedMonster = {
uuid: item.uuid,
type: MonType.Melee, // 简化的兜底,真实逻辑依赖 heroSet 配置
hp: base ? base.hp : 100,
ap: base ? base.ap : 10,
affixes: [],
isBoss: isBoss,
spawnIndex: 0
};
this.addMonsterAtGrid(targetPosIndex, monData, item.level);
}
// ======================== 波次管理 ======================== // ======================== 波次管理 ========================
/** /**
* 开始下一波: * 开始下一波:波数 +1 并预生成数据
* 1. 波数 +1 并更新全局数据。
* 2. 分发 NewWave 事件(实际的生成在 resetSlotSpawnData 中触发)。
*/ */
private onTimeUpAdvanceWave() { private onTimeUpAdvanceWave() {
this.currentWave += 1; this.currentWave += 1;
smc.vmdata.mission_data.level = this.currentWave; smc.vmdata.mission_data.level = this.currentWave;
// 预生成新一波数据以获取数量和 Boss 信息
const monsters = spawningEngine.generateWave(this.currentWave); const monsters = spawningEngine.generateWave(this.currentWave);
this.setupWaveData(monsters); this.setupWaveData(monsters);
} }
private onPhasePrepareEnd() { private onPhasePrepareEnd() {
this.resetSlotSpawnData(this.currentWave); this.resetSlotSpawnData();
// 准备结束阶段,立即刷出本波所有怪物 // 准备结束阶段,立即刷出本波所有怪物
if (this.pendingMonsters.length > 0) { if (this.pendingMonsters.length > 0) {
@@ -262,11 +149,9 @@ export class MissionMonCompComp extends CCComp {
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const monData = this.pendingMonsters.shift()!; const monData = this.pendingMonsters.shift()!;
const targetPosIndex = this.waveSpawnedCount % MissionMonCompComp.MAX_MONSTERS; const targetPosIndex = this.waveSpawnedCount % MissionMonCompComp.MAX_MONSTERS;
console.log(`[MissionMonComp] [PhasePrepareEnd] 准备生成怪物 UUID=${monData.uuid}, 当前已生成数量=${this.waveSpawnedCount}`); this.addMonsterAtGrid(targetPosIndex, monData, this.currentWave);
this.addMonsterAtGrid(targetPosIndex, monData);
this.waveSpawnedCount++; this.waveSpawnedCount++;
} }
// 生成完毕后清空 pendingMonsters
this.pendingMonsters = []; this.pendingMonsters = [];
} }
} }
@@ -274,14 +159,9 @@ export class MissionMonCompComp extends CCComp {
// ======================== 槽位管理 ======================== // ======================== 槽位管理 ========================
/** /**
* 重新分配本波所有怪物状态: * 清理上一波残留怪物,并重置生成计数
* 1. 清理上一波残留怪物。
* 2. pendingMonsters 已在 onTimeUpAdvanceWave / fight_ready 中准备好。
*
* @param wave 当前波数
*/ */
private resetSlotSpawnData(wave: number = 1) { private resetSlotSpawnData() {
// 1. 清理上一波残留怪物
ecs.query(ecs.allOf(HeroAttrsComp)).forEach(e => { ecs.query(ecs.allOf(HeroAttrsComp)).forEach(e => {
const attrs = e.get(HeroAttrsComp); const attrs = e.get(HeroAttrsComp);
if (attrs && attrs.fac === FacSet.MON && !attrs.is_dead) { if (attrs && attrs.fac === FacSet.MON && !attrs.is_dead) {
@@ -289,18 +169,13 @@ export class MissionMonCompComp extends CCComp {
} }
}); });
// 2. 重置排号索引
this.waveSpawnedCount = 0; this.waveSpawnedCount = 0;
} }
// ======================== 怪物生成 ======================== // ======================== 怪物生成 ========================
/** /**
* 在指定位置索引处生成一个怪物 * 在指定位置索引处生成一个怪物
*
* @param posIndex 位置索引 (0-11)
* @param monData 新引擎生成的怪物数据 (含 uuid, hp, ap, affixes 等)
* @param monLv 怪物等级 (仅对旧有的 level 参数做兼容,实际属性由 monData 决定)
*/ */
private addMonsterAtGrid( private addMonsterAtGrid(
posIndex: number, posIndex: number,
@@ -310,51 +185,32 @@ export class MissionMonCompComp extends CCComp {
let mon = ecs.getEntity<Monster>(Monster); let mon = ecs.getEntity<Monster>(Monster);
let scale = -1; let scale = -1;
// 获取硬编码的占位点坐标,不再使用随机偏移
const basePos = MissionMonCompComp.MON_POSITIONS[posIndex % MissionMonCompComp.MON_POSITIONS.length]; const basePos = MissionMonCompComp.MON_POSITIONS[posIndex % MissionMonCompComp.MON_POSITIONS.length];
const spawnX = basePos.x; const spawnX = basePos.x;
const landingY = basePos.y + (monData.isBoss ? 6 : 0); const landingY = basePos.y + (monData.isBoss ? 6 : 0);
const spawnPos: Vec3 = v3(spawnX, landingY + MissionMonCompComp.MON_DROP_HEIGHT, 0); const spawnPos: Vec3 = v3(spawnX, landingY + MissionMonCompComp.MON_DROP_HEIGHT, 0);
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999; this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999;
// 如果存在测试技能覆盖,则传递下去(修改 mon.load 逻辑或者通过预存) if (monData.testSkills) {
// 为了避免侵入 Mon.ts 的原有逻辑,我们先预存
(mon as any)._testSkills = monData.testSkills; (mon as any)._testSkills = monData.testSkills;
}
mon.load(spawnPos, scale, monData.uuid, monData.isBoss, landingY, monLv, posIndex); mon.load(spawnPos, scale, monData.uuid, monData.isBoss, landingY, monLv, posIndex);
// 设置渲染排序
const move = mon.get(MonMoveComp); const move = mon.get(MonMoveComp);
if (move) { if (move) {
move.spawnOrder = this.globalSpawnOrder; move.spawnOrder = this.globalSpawnOrder;
} }
// 应用新引擎计算好的最终属性和词缀 // 应用新引擎计算好的最终属性
const model = mon.get(HeroAttrsComp); const model = mon.get(HeroAttrsComp);
if (!model) return; if (model) {
model.ap = monData.ap; model.ap = monData.ap;
model.hp_max = monData.hp; model.hp_max = monData.hp;
model.hp = model.hp_max; model.hp = model.hp_max;
// 将词缀记录到属性组件上,供战斗层使用
(model as any).affixes = monData.affixes || [];
// 解析特定的抗性词缀
if (monData.affixes) {
if (monData.affixes.includes(AffixType.CritRes)) {
model.critical_res = 50;
}
if (monData.affixes.includes(AffixType.FreezeRes)) {
model.freeze_res = 50;
}
if (monData.affixes.includes(AffixType.KnockbackRes)) {
model.knockback_res = 50;
}
} }
} }
/** ECS 组件移除时触发(当前不销毁节点) */ /** ECS 组件移除时触发 */
reset() { reset() {}
// this.node.destroy();
}
} }

File diff suppressed because it is too large Load Diff