From 083a530a724d4634a08d15ed012259067375e240 Mon Sep 17 00:00:00 2001 From: panw Date: Wed, 15 Apr 2026 15:48:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=88=98=E6=96=97):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E6=88=98=E6=96=97=E6=97=B6=E9=97=B4=E7=BB=93=E6=9D=9F=E5=92=8C?= =?UTF-8?q?=E6=80=AA=E7=89=A9=E5=85=A8=E7=81=AD=E8=87=AA=E5=8A=A8=E6=8E=A8?= =?UTF-8?q?=E8=BF=9B=E6=B3=A2=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将战斗时间从600秒改为30秒,便于测试 - 战斗倒计时归零或场上怪物全灭时,自动结束当前波次并进入准备阶段 - 怪物波次切换时,新怪物继承被覆盖旧怪物的部分属性(生命值和攻击力) - 调整波次初始化逻辑,确保战斗时间在每波开始时重置 - 新增事件 PhasePrepareEnd 和 TimeUpAdvanceWave 协调阶段切换 --- assets/script/game/common/config/GameSet.ts | 2 +- assets/script/game/map/MissionComp.ts | 15 ++++ assets/script/game/map/MissionMonComp.ts | 84 ++++++++++++++++----- 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/assets/script/game/common/config/GameSet.ts b/assets/script/game/common/config/GameSet.ts index e82ad8f0..d1cba297 100644 --- a/assets/script/game/common/config/GameSet.ts +++ b/assets/script/game/common/config/GameSet.ts @@ -30,7 +30,7 @@ export enum FightSet { LVUP_GOLD_UP=50,//升级需要的金币 CHOU_GOLD=100,//抽卡需要的金币 BACK_RANG=30,//后退范围 - FiIGHT_TIME=60*10,//战斗时间 + FiIGHT_TIME=30,//战斗时间 BACK_CHANCE=40,//击退概率 FROST_TIME=3,//冰冻时间 SKILL_CAST_DELAY=0.15, diff --git a/assets/script/game/map/MissionComp.ts b/assets/script/game/map/MissionComp.ts index e44f10b9..81a5f5c1 100644 --- a/assets/script/game/map/MissionComp.ts +++ b/assets/script/game/map/MissionComp.ts @@ -214,6 +214,11 @@ export class MissionComp extends CCComp { smc.vmdata.mission_data.fight_time+=dt this.FightTime-=dt this.update_time(); + if (this.FightTime <= 0) { + // 时间到了,自动结束战斗进入准备阶段 + this.FightTime = FightSet.FiIGHT_TIME; + oops.message.dispatchEvent("TimeUpAdvanceWave"); + } } } @@ -321,6 +326,7 @@ export class MissionComp extends CCComp { case MissionPhase.PrepareEnd: if (this.start_btn && this.start_btn.isValid) this.start_btn.active = false; + oops.message.dispatchEvent("PhasePrepareEnd"); break; case MissionPhase.BattleStart: @@ -558,6 +564,7 @@ export class MissionComp extends CCComp { smc.vmdata.mission_data.level = wave; this.grantPrepareCoinByWave(wave); this.lastTimeSecond = -1; + this.FightTime = FightSet.FiIGHT_TIME; this.update_time(); // 检查并推送卡池升级事件 @@ -639,6 +646,14 @@ export class MissionComp extends CCComp { } }); this.handleHeroWipe(heroCount); + + // 怪物全灭检测:如果战斗阶段场上没有任何活着的怪物,直接结束战斗进入下一波的准备阶段 + if (monsterCount === 0 && smc.mission.play && !smc.mission.pause && this.currentPhase === MissionPhase.Battle) { + this.FightTime = FightSet.FiIGHT_TIME; + oops.message.dispatchEvent("TimeUpAdvanceWave"); + return; + } + smc.vmdata.mission_data.mon_num = monsterCount; const { max, resume } = this.getMonsterThresholds(); smc.vmdata.mission_data.mon_max = max; diff --git a/assets/script/game/map/MissionMonComp.ts b/assets/script/game/map/MissionMonComp.ts index 69986e82..231024e6 100644 --- a/assets/script/game/map/MissionMonComp.ts +++ b/assets/script/game/map/MissionMonComp.ts @@ -103,6 +103,8 @@ export class MissionMonCompComp extends CCComp { onLoad(){ this.on(GameEvent.FightReady,this.fight_ready,this) this.on("SpawnSpecialMonster", this.onSpawnSpecialMonster, this); + this.on("PhasePrepareEnd", this.onPhasePrepareEnd, this); + this.on("TimeUpAdvanceWave", this.onTimeUpAdvanceWave, this); this.resetSlotSpawnData(1) } @@ -119,7 +121,6 @@ export class MissionMonCompComp extends CCComp { if(smc.mission.stop_mon_action) return; if(!smc.mission.in_fight) return; this.refreshSlotOccupancy(); - this.tryAdvanceWave(); if(!smc.mission.in_fight) return; if(smc.mission.stop_spawn_mon) return; this.updateSpecialQueue(dt); @@ -154,11 +155,25 @@ export class MissionMonCompComp extends CCComp { smc.mission.stop_spawn_mon = false this.globalSpawnOrder = 0 this.queueTimer = 0 - this.currentWave = 0 + this.currentWave = 1 this.waveTargetCount = 0 this.waveSpawnedCount = 0 this.MonQueue = [] - this.startNextWave() + + let hasBoss = false; + const config = WaveSlotConfig[this.currentWave] || DefaultWaveSlot; + for (const slot of config) { + if (slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss) { + hasBoss = true; + } + } + oops.message.dispatchEvent(GameEvent.NewWave, { + wave: this.currentWave, + total: this.waveTargetCount, + bossWave: hasBoss, + }); + + // 不再直接调用 startNextWave(),等待进入 PrepareEnd 阶段再刷怪 mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System"); } @@ -224,10 +239,9 @@ export class MissionMonCompComp extends CCComp { * 2. 重置槽位并根据配置生成本波所有怪物。 * 3. 分发 NewWave 事件。 */ - private startNextWave() { + private onTimeUpAdvanceWave() { this.currentWave += 1; smc.vmdata.mission_data.level = this.currentWave; - this.resetSlotSpawnData(this.currentWave); // 检查本波是否有 Boss let hasBoss = false; @@ -240,20 +254,13 @@ export class MissionMonCompComp extends CCComp { oops.message.dispatchEvent(GameEvent.NewWave, { wave: this.currentWave, - total: this.waveTargetCount, + total: this.waveTargetCount, // 此时还是上一波的怪物数量,但可以不传或后续修正 bossWave: hasBoss, }); } - /** - * 尝试推进波次: - * 条件:队列为空 + 所有槽位无活怪 + 全局怪物数为 0。 - */ - private tryAdvanceWave() { - if (this.MonQueue.length > 0) return; - if (this.hasActiveSlotMonster()) return; - if (smc.vmdata.mission_data.mon_num > 0) return; - this.startNextWave(); + private onPhasePrepareEnd() { + this.resetSlotSpawnData(this.currentWave); } /** 获取当前阶段(stage = wave - 1,用于属性成长计算) */ @@ -316,6 +323,7 @@ export class MissionMonCompComp extends CCComp { */ private resetSlotSpawnData(wave: number = 1) { const config: IWaveSlot[] = WaveSlotConfig[wave] || DefaultWaveSlot; + const oldOccupiedEids = [...this.slotOccupiedEids]; this.slotOccupiedEids = Array(MissionMonCompComp.MAX_SLOTS).fill(null); let allMons: any[] = []; @@ -368,11 +376,49 @@ export class MissionMonCompComp extends CCComp { } } + let absorbedEids = new Set(); + // 立即生成本波所有怪物 for (let i = 0; i < MissionMonCompComp.MAX_SLOTS; i++) { const req = assignedSlots[i]; if (req && req !== "occupied") { - this.addMonsterBySlot(i, req.uuid, req.isBoss, req.upType, req.monLv, req.slotsPerMon); + let inheritedHp = 0; + let inheritedAp = 0; + + for (let j = 0; j < req.slotsPerMon; j++) { + let oldEid = oldOccupiedEids[i + j]; + if (oldEid && !absorbedEids.has(oldEid)) { + absorbedEids.add(oldEid); + const entity = ecs.getEntityByEid(oldEid); + if (entity) { + const attrs = entity.get(HeroAttrsComp); + if (attrs && attrs.hp > 0 && !attrs.is_dead) { + inheritedHp += attrs.hp; + inheritedAp += Math.floor(attrs.ap / 2); + } + entity.destroy(); + } + } + oldOccupiedEids[i + j] = null; + } + + this.addMonsterBySlot(i, req.uuid, req.isBoss, req.upType, req.monLv, req.slotsPerMon, inheritedHp, inheritedAp); + } + } + + // 清理被覆盖但没有被新怪占用槽位的旧怪(或者保留它们) + // 按照需求,保留未被覆盖的旧怪物 + for (let i = 0; i < MissionMonCompComp.MAX_SLOTS; i++) { + if (oldOccupiedEids[i]) { + const entity = ecs.getEntityByEid(oldOccupiedEids[i]!); + if (entity) { + const attrs = entity.get(HeroAttrsComp); + if (attrs && attrs.hp > 0 && !attrs.is_dead) { + this.slotOccupiedEids[i] = oldOccupiedEids[i]; + } else { + entity.destroy(); + } + } } } } @@ -430,6 +476,8 @@ export class MissionMonCompComp extends CCComp { upType: UpType = UpType.AP1_HP1, monLv: number = 1, slotsPerMon: number = 1, + inheritedHp: number = 0, + inheritedAp: number = 0, ) { let mon = ecs.getEntity(Monster); let scale = -1; @@ -464,8 +512,8 @@ export class MissionMonCompComp extends CCComp { const stage = this.getCurrentStage(); const grow = this.resolveGrowPair(upType, isBoss); const bias = Math.max(0.1, this.getSpawnPowerBias()); - model.ap = Math.max(1, Math.floor((base.ap + stage * grow[0]) * bias)); - model.hp_max = Math.max(1, Math.floor((base.hp + stage * grow[1]) * bias)); + model.ap = Math.max(1, Math.floor((base.ap + stage * grow[0]) * bias)) + inheritedAp; + model.hp_max = Math.max(1, Math.floor((base.hp + stage * grow[1]) * bias)) + inheritedHp; model.hp = model.hp_max; }