Files
pixelheros/assets/script/game/map/MissionMonComp.ts
panFD 05dc771d4e fix: 调整游戏战斗站位与UI显示参数
1. 修正游戏地平线GAME_LINE数值,调整英雄和怪物的出生站位
2. 修改shadow预制体的缩放、尺寸和填充模式参数
3. 调整地图预制体的元素位置和尺寸
4. 优化技能动画的缩放参数和编辑器冗余配置
2026-07-20 22:52:43 +08:00

217 lines
7.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file MissionMonComp.ts
* @description 怪物Monster波次刷新管理组件逻辑层
*
* 职责:
* 1. 管理每一波怪物的生成计划:根据 RogueConfig 生成怪物。
* 2. 自动推进波次在准备阶段结束时PhasePrepareEnd统一刷出怪物。
*
* 关键设计:
* - 采用 12 个硬编码的网格位置点 (MON_POSITIONS3行x4列)
* - 每次生成最多 12 个怪物,固定在位置点。
* - 上一波残留怪在波次结束/开始时统一清理。
*/
import { _decorator, v3, Vec3 } from "cc";
import { mLogger } from "../common/Logger";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { Monster } from "../hero/Mon";
import { smc } from "../common/SingletonModuleComp";
import { GameEvent } from "../common/config/GameEvent";
import { BoxSet, FacSet } from "../common/config/GameSet";
import { spawningEngine, GeneratedMonster, TestModeConfig } from "./RogueConfig";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { MonMoveComp } from "../hero/MonMoveComp";
const { ccclass, property } = _decorator;
@ccclass('MissionMonCompComp')
@ecs.register('MissionMonComp', false)
export class MissionMonCompComp extends CCComp {
// ======================== 常量 ========================
/** 怪物最多 12 个 */
private static readonly MAX_MONSTERS = 12;
/** 怪物出生掉落高度 */
private static readonly MON_DROP_HEIGHT = 0;
/** 硬编码的 12 个怪物占位点 (3行4列) */
public static readonly MON_POSITIONS: Vec3[] = [
// 第 1 列 (X=60)
v3(0, BoxSet.GAME_LINE, 0), // index 0: Top
v3(0, BoxSet.GAME_LINE, 0), // index 1: Mid
v3(0, BoxSet.GAME_LINE, 0), // index 2: Bot
// 第 2 列 (X=140)
v3(90, BoxSet.GAME_LINE, 0), // index 3: Top
v3(90, BoxSet.GAME_LINE, 0), // index 4: Mid
v3(90, BoxSet.GAME_LINE, 0), // index 5: Bot
// 第 3 列 (X=220)
v3(180, BoxSet.GAME_LINE, 0), // index 6: Top
v3(180, BoxSet.GAME_LINE, 0), // index 7: Mid
v3(180, BoxSet.GAME_LINE, 0), // index 8: Bot
// 第 4 列 (X=300)
v3(270, BoxSet.GAME_LINE, 0), // index 9: Top
v3(270, BoxSet.GAME_LINE, 0), // index 10: Mid
v3(270, BoxSet.GAME_LINE, 0), // index 11: Bot
];
// ======================== 编辑器属性 ========================
@property({ tooltip: "是否启用调试日志" })
private debugMode: boolean = false;
// ======================== 运行时状态 ========================
/** 全局生成顺序计数器(用于渲染层级排序) */
private globalSpawnOrder: number = 0;
/** 当前波数 */
private currentWave: number = 0;
/** 当前波的目标怪物总数 */
private waveTargetCount: number = 0;
/** 当前波已生成的怪物数量 */
private waveSpawnedCount: number = 0;
/** 等待生成的怪物队列 */
private pendingMonsters: GeneratedMonster[] = [];
// ======================== 生命周期 ========================
onLoad() {
this.on(GameEvent.FightReady, this.fight_ready, this);
this.on("PhasePrepareEnd", this.onPhasePrepareEnd, this);
this.on("TimeUpAdvanceWave", this.onTimeUpAdvanceWave, this);
}
protected update(dt: number): void {
smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length;
}
start() {}
private setupWaveData(monsters: GeneratedMonster[]) {
this.pendingMonsters = monsters.slice(0, MissionMonCompComp.MAX_MONSTERS);
smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length;
this.waveTargetCount = this.pendingMonsters.length;
let hasBoss = monsters.some(m => m.isBoss);
mLogger.log(this.debugMode, 'MissionMonComp', `[MissionMonComp] 波次 ${this.currentWave} 生成怪物总数: ${this.waveTargetCount}`);
oops.message.dispatchEvent(GameEvent.NewWave, {
wave: this.currentWave,
total: this.waveTargetCount,
bossWave: hasBoss,
});
}
/**
* 战斗准备:重置所有运行时状态并开始第一波。
*/
fight_ready() {
smc.vmdata.mission_data.mon_num = 0;
smc.mission.stop_spawn_mon = false;
this.globalSpawnOrder = 0;
this.currentWave = 1;
this.waveTargetCount = 0;
this.waveSpawnedCount = 0;
this.pendingMonsters = [];
// 预生成第一波数据以获取数量和 Boss 信息
const monsters = spawningEngine.generateWave(this.currentWave);
this.setupWaveData(monsters);
if (TestModeConfig.enable) {
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] 测试模式已开启");
}
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System");
}
// ======================== 波次管理 ========================
/**
* 开始下一波:波数 +1 并预生成数据
*/
private onTimeUpAdvanceWave() {
this.currentWave += 1;
smc.vmdata.mission_data.level = this.currentWave;
const monsters = spawningEngine.generateWave(this.currentWave);
this.setupWaveData(monsters);
}
private onPhasePrepareEnd() {
this.resetSlotSpawnData();
// 准备结束阶段,立即刷出本波所有怪物
if (this.pendingMonsters.length > 0) {
let count = Math.min(this.pendingMonsters.length, MissionMonCompComp.MAX_MONSTERS);
for (let i = 0; i < count; i++) {
const monData = this.pendingMonsters.shift()!;
const targetPosIndex = this.waveSpawnedCount % MissionMonCompComp.MAX_MONSTERS;
this.addMonsterAtGrid(targetPosIndex, monData, this.currentWave);
this.waveSpawnedCount++;
}
this.pendingMonsters = [];
}
}
// ======================== 槽位管理 ========================
/**
* 清理上一波残留怪物,并重置生成计数
*/
private resetSlotSpawnData() {
ecs.query(ecs.allOf(HeroAttrsComp)).forEach(e => {
const attrs = e.get(HeroAttrsComp);
if (attrs && attrs.fac === FacSet.MON && !attrs.is_dead) {
e.destroy();
}
});
this.waveSpawnedCount = 0;
}
// ======================== 怪物生成 ========================
/**
* 在指定位置索引处生成一个怪物
*/
private addMonsterAtGrid(
posIndex: number,
monData: GeneratedMonster,
monLv: number = 1
) {
let mon = ecs.getEntity<Monster>(Monster);
let scale = -1;
const basePos = MissionMonCompComp.MON_POSITIONS[posIndex % MissionMonCompComp.MON_POSITIONS.length];
const spawnX = basePos.x;
const landingY = basePos.y + (monData.isBoss ? 6 : 0);
const spawnPos: Vec3 = v3(spawnX, landingY + MissionMonCompComp.MON_DROP_HEIGHT, 0);
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999;
if (monData.testSkills) {
(mon as any)._testSkills = monData.testSkills;
}
mon.load(spawnPos, scale, monData.uuid, monData.isBoss, landingY, monLv, posIndex);
const move = mon.get(MonMoveComp);
if (move) {
move.spawnOrder = this.globalSpawnOrder;
}
// 应用新引擎计算好的最终属性
const model = mon.get(HeroAttrsComp);
if (model) {
model.ap = monData.ap;
model.hp_max = monData.hp;
model.hp = model.hp_max;
}
}
/** ECS 组件移除时触发 */
reset() {}
}