feat(关卡): 实现基于波次的怪物生成系统
- 将时间轴刷怪改为波次制,每波生成固定数量普通怪 - 每若干波生成一个Boss,Boss波次可配置 - 在界面时间显示前添加当前波次信息 - 添加新波次开始时的事件通知机制 - 调整卡片预制件的Y坐标以适应新布局
This commit is contained in:
@@ -2294,7 +2294,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -108.057,
|
||||
"y": -136.25,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -2592,7 +2592,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -108.057,
|
||||
"y": -136.25,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,8 @@ export class MissionCardComp extends CCComp {
|
||||
private readonly buttonClickScale: number = 1.06;
|
||||
/** 四个插卡槽位(固定顺序分发:1~4) */
|
||||
@property(Node)
|
||||
cards_node:Node = null!
|
||||
@property(Node)
|
||||
card1:Node = null!
|
||||
@property(Node)
|
||||
card2:Node = null!
|
||||
|
||||
@@ -60,6 +60,7 @@ export class MissionComp extends CCComp {
|
||||
private heapTrendTimer: number = 0;
|
||||
private heapTrendBaseMB: number = -1;
|
||||
private monsterCountSyncTimer: number = 0;
|
||||
private currentWave: number = 0;
|
||||
private readonly heroViewMatcher = ecs.allOf(HeroViewComp);
|
||||
private readonly skillViewMatcher = ecs.allOf(SkillView);
|
||||
private readonly heroAttrsMatcher = ecs.allOf(HeroAttrsComp);
|
||||
@@ -72,6 +73,7 @@ export class MissionComp extends CCComp {
|
||||
// this.on(GameEvent.HeroDead,this.do_hero_dead,this)
|
||||
// this.on(GameEvent.FightEnd,this.fight_end,this)
|
||||
this.on(GameEvent.MissionEnd,this.mission_end,this)
|
||||
this.on(GameEvent.NewWave,this.onNewWave,this)
|
||||
this.on(GameEvent.DO_AD_BACK,this.do_ad,this)
|
||||
this.removeMemoryPanel()
|
||||
}
|
||||
@@ -94,7 +96,8 @@ export class MissionComp extends CCComp {
|
||||
this.lastTimeSecond = remainSecond;
|
||||
let m = Math.floor(remainSecond / 60);
|
||||
let s = remainSecond % 60;
|
||||
let str = `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
||||
const wave = Math.max(1, this.currentWave || smc.vmdata.mission_data.level || 1);
|
||||
let str = `W${wave.toString().padStart(2, '0')} ${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
||||
if(str != this.lastTimeStr){
|
||||
this.time_node.getChildByName("time").getComponent(Label).string = str;
|
||||
this.lastTimeStr = str;
|
||||
@@ -125,10 +128,9 @@ export class MissionComp extends CCComp {
|
||||
this.unscheduleAllCallbacks();
|
||||
// 确保清理上一局的残留实体
|
||||
this.cleanComponents();
|
||||
|
||||
oops.message.dispatchEvent(GameEvent.FightReady)
|
||||
this.node.active=true
|
||||
this.data_init()
|
||||
oops.message.dispatchEvent(GameEvent.FightReady)
|
||||
let loading=this.node.parent.getChildByName("loading")
|
||||
loading.active=true
|
||||
this.scheduleOnce(()=>{
|
||||
@@ -197,6 +199,7 @@ export class MissionComp extends CCComp {
|
||||
smc.vmdata.mission_data.mon_num=0
|
||||
smc.vmdata.mission_data.level=0
|
||||
smc.vmdata.mission_data.mon_max = Math.max(1, Math.floor(this.maxMonsterCount))
|
||||
this.currentWave = 0;
|
||||
this.FightTime=FightSet.FiIGHT_TIME
|
||||
this.rewards=[] // 改为数组,用于存储掉落物品列表
|
||||
this.revive_times = 1; // 每次任务开始重置复活次数
|
||||
@@ -221,6 +224,15 @@ export class MissionComp extends CCComp {
|
||||
// mLogger.log(this.debugMode, 'MissionComp', "局内数据初始化",smc.vmdata.mission_data)
|
||||
}
|
||||
|
||||
private onNewWave(event: string, data: any) {
|
||||
const wave = Number(data?.wave ?? 0);
|
||||
if (wave <= 0) return;
|
||||
this.currentWave = wave;
|
||||
smc.vmdata.mission_data.level = wave;
|
||||
this.lastTimeSecond = -1;
|
||||
this.update_time();
|
||||
}
|
||||
|
||||
private getMonsterThresholds(): { max: number; resume: number } {
|
||||
const max = Math.max(1, Math.floor(this.maxMonsterCount));
|
||||
const resume = Math.min(max - 1, Math.max(0, Math.floor(this.resumeMonsterCount)));
|
||||
|
||||
@@ -2,12 +2,13 @@ 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 { HeroInfo, MonStart } from "../common/config/heroSet";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import {BoxSet } from "../common/config/GameSet";
|
||||
import { BossList, BossSpawnTimeline, MonList, MonType, SpawnBaseCd, SpawnMinCd, SpawnPowerBias, SpawnStageReduce, StageBossGrow, StageDuration, StageGrow, UpType } from "./RogueConfig";
|
||||
import { BossList, MonList, MonType, SpawnPowerBias, StageBossGrow, StageGrow, UpType } from "./RogueConfig";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
import { MoveComp } from "../hero/MoveComp";
|
||||
const { ccclass, property } = _decorator;
|
||||
@@ -20,6 +21,14 @@ export class MissionMonCompComp extends CCComp {
|
||||
private static readonly MON_DROP_HEIGHT = 300;
|
||||
@property({ tooltip: "是否启用调试日志" })
|
||||
private debugMode: boolean = false;
|
||||
@property({ tooltip: "每波基础普通怪数量" })
|
||||
private baseMonstersPerWave: number = 5;
|
||||
@property({ tooltip: "每波额外增加普通怪数量" })
|
||||
private waveMonsterGrowth: number = 1;
|
||||
@property({ tooltip: "多少波刷新一次 Boss" })
|
||||
private bossWaveInterval: number = 5;
|
||||
@property({ tooltip: "同一波内刷怪间隔(秒)" })
|
||||
private waveSpawnCd: number = 0.35;
|
||||
|
||||
// 刷怪队列(用于插队生成:比如运营活动怪、技能召唤怪、剧情强制怪)
|
||||
// 约定:队列里的怪会优先于常规刷新处理
|
||||
@@ -31,18 +40,15 @@ export class MissionMonCompComp extends CCComp {
|
||||
private spawnCount: number = 0; // 总生成计数,用于控制横向分布位置
|
||||
/** 全局生成顺序计数器,用于层级管理(预留) */
|
||||
private globalSpawnOrder: number = 0;
|
||||
/** 局内战斗运行时间(秒) */
|
||||
private gameTime: number = 0;
|
||||
/** 普通怪刷新计时器 */
|
||||
private waveTimer: number = 0;
|
||||
/** 插队刷怪处理计时器 */
|
||||
private queueTimer: number = 0;
|
||||
private nextBossSpawnIndex: 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(GameEvent.NewWave,this.fight_ready,this)
|
||||
// 支持外部模块主动推入特殊怪(优先级高于常规刷新)
|
||||
this.on("SpawnSpecialMonster", this.onSpawnSpecialMonster, this);
|
||||
}
|
||||
|
||||
@@ -65,34 +71,30 @@ export class MissionMonCompComp extends CCComp {
|
||||
}
|
||||
|
||||
fight_ready(){
|
||||
// 仅重置“本组件”刷怪状态,不处理其他系统状态
|
||||
smc.vmdata.mission_data.mon_num=0
|
||||
smc.mission.stop_spawn_mon = false
|
||||
this.globalSpawnOrder = 0
|
||||
this.gameTime = 0
|
||||
this.waveTimer = 0
|
||||
this.queueTimer = 0
|
||||
this.nextBossSpawnIndex = 0
|
||||
this.waveSpawnTimer = 0
|
||||
this.currentWave = 0
|
||||
this.waveTargetCount = 0
|
||||
this.waveSpawnedCount = 0
|
||||
this.bossSpawnedInWave = false
|
||||
this.MonQueue = []
|
||||
this.spawnCount = 0
|
||||
|
||||
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System (15-min Cycle)");
|
||||
this.startNextWave()
|
||||
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System");
|
||||
}
|
||||
|
||||
protected update(dt: number): void {
|
||||
// 统一战斗状态门禁:未开战/暂停/角色死亡阶段均不刷怪
|
||||
if(!smc.mission.play) return
|
||||
if(smc.mission.pause) return
|
||||
if(smc.mission.stop_mon_action) return;
|
||||
if(!smc.mission.in_fight) return;
|
||||
|
||||
// 计时推进:所有“按时间驱动”的曲线都依赖 gameTime
|
||||
this.gameTime += dt;
|
||||
this.updateBossSpawn();
|
||||
this.tryAdvanceWave();
|
||||
if(smc.mission.stop_spawn_mon) return;
|
||||
this.updateSpecialQueue(dt);
|
||||
this.updateNormalSpawn(dt);
|
||||
|
||||
this.updateWaveSpawn(dt);
|
||||
}
|
||||
|
||||
private updateSpecialQueue(dt: number) {
|
||||
@@ -109,43 +111,55 @@ export class MissionMonCompComp extends CCComp {
|
||||
this.spawnCount += 1;
|
||||
}
|
||||
|
||||
private updateNormalSpawn(dt: number) {
|
||||
this.waveTimer += dt;
|
||||
// 普通怪刷新间隔会随 stage 增长而缩短
|
||||
const spawnCd = this.getCurrentSpawnCd();
|
||||
if (this.waveTimer < spawnCd) return;
|
||||
this.waveTimer = 0;
|
||||
// 先随机怪物类型,再在该类型池内随机具体 uuid
|
||||
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.addMonster(bossUuid, this.spawnCount, true, bossUpType);
|
||||
this.spawnCount += 1;
|
||||
this.bossSpawnedInWave = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const uuid = this.getRandomNormalMonsterUuid();
|
||||
// 每只怪独立抽取成长方案,使同时间段怪群有轻微离散性
|
||||
const upType = this.getRandomUpType();
|
||||
this.addMonster(uuid, this.spawnCount, false, upType);
|
||||
this.waveSpawnedCount += 1;
|
||||
this.spawnCount += 1;
|
||||
}
|
||||
|
||||
private updateBossSpawn() {
|
||||
while (
|
||||
this.nextBossSpawnIndex < BossSpawnTimeline.length &&
|
||||
this.gameTime >= BossSpawnTimeline[this.nextBossSpawnIndex]
|
||||
) {
|
||||
const uuid = this.getRandomBossUuid();
|
||||
const upType = this.getRandomUpType();
|
||||
this.addMonster(uuid, this.spawnCount, true, upType);
|
||||
this.spawnCount += 1;
|
||||
this.nextBossSpawnIndex += 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;
|
||||
oops.message.dispatchEvent(GameEvent.NewWave, {
|
||||
wave: this.currentWave,
|
||||
total: this.waveTargetCount,
|
||||
bossWave: this.isBossWave(),
|
||||
});
|
||||
}
|
||||
|
||||
private tryAdvanceWave() {
|
||||
if (this.waveSpawnedCount < this.waveTargetCount) return;
|
||||
if (this.isBossWave() && !this.bossSpawnedInWave) 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 {
|
||||
// 每 30 秒提升 1 阶(由配置 StageDuration 控制)
|
||||
return Math.floor(this.gameTime / StageDuration);
|
||||
}
|
||||
|
||||
private getCurrentSpawnCd(): number {
|
||||
const stage = this.getCurrentStage();
|
||||
const cd = SpawnBaseCd - stage * SpawnStageReduce;
|
||||
// 保护下限,避免后期刷新间隔过小打爆性能
|
||||
return Math.max(SpawnMinCd, cd);
|
||||
return Math.max(0, this.currentWave - 1);
|
||||
}
|
||||
|
||||
private getRandomUpType(): UpType {
|
||||
|
||||
Reference in New Issue
Block a user