feat: 完成肉鸽回合制游戏核心玩法迭代
新增连杀系统、金币飞行特效、波次HUD、屏幕震动等表现功能,重构怪物刷出逻辑与难度动态调节,调整回合回血比例,优化游戏节奏与体验。 主要变更: 1. 调整回合回血比例从0.5到0.4,优化前期节奏 2. 新增连杀计数与奖励系统,支持5/10/20连杀触发对应表现 3. 实现怪物死亡掉落金币的抛物线飞行特效 4. 增加波次进度HUD,显示剩余怪物与全局回合进度 5. 新增屏幕震动工具与战斗横幅统一展示系统 6. 重构怪物刷出逻辑,支持按回合类型调整刷怪间隔,加入清场加速机制 7. 优化动态难度调节算法,增加滞回与指数平滑,避免难度突变 8. 新增Boss预警、登场事件与回合清屏事件,完善事件总线 9. 调整怪物数量阈值与回合倒计时档位,适配新的节奏设计
This commit is contained in:
@@ -48,7 +48,7 @@ import { Tooltip } from "../skill/Tooltip";
|
||||
import { Timer } from "db://oops-framework/core/common/timer/Timer";
|
||||
import { FieldSkillType } from "../common/config/SkillSet";
|
||||
import { FieldSkillHelper } from "../hero/FieldSkillHelper";
|
||||
import { spawningEngine, MAX_WAVE, DynamicTuner } from "./RogueConfig";
|
||||
import { spawningEngine, MAX_WAVE, DynamicTuner, WAVE_TIMEOUT, WAVE_TIMEOUT_BOSS, BATCH_INTERVAL, BATCH_COUNT, WAVE_DURATION } from "./RogueConfig";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** 任务(关卡)生命周期阶段 */
|
||||
@@ -82,10 +82,10 @@ export class MissionComp extends CCComp {
|
||||
|
||||
// ======================== 配置参数 ========================
|
||||
|
||||
/** 怪物数量上限(超过后暂停刷怪) */
|
||||
private maxMonsterCount: number = 80;
|
||||
/** 怪物数量恢复阈值(降至此值以下恢复刷怪) */
|
||||
private resumeMonsterCount: number = 45;
|
||||
/** 怪物数量上限(超过后暂停刷怪):略高于单回合上限 MAX_MONSTERS(54),只有跨回合堆积才触发泄压 */
|
||||
private maxMonsterCount: number = 60;
|
||||
/** 怪物数量恢复阈值(降至此值以下恢复刷怪,需留空间接纳后续批次,单批最大约 18 只) */
|
||||
private resumeMonsterCount: number = 40;
|
||||
|
||||
// ======================== 编辑器绑定节点 ========================
|
||||
|
||||
@@ -128,8 +128,12 @@ export class MissionComp extends CCComp {
|
||||
PhaseTime: Timer = new Timer(1)
|
||||
/** 回合间倒计时(秒) */
|
||||
private waveCountdown: number = 0;
|
||||
/** 回合间倒计时总时长(秒) */
|
||||
private readonly WAVE_COUNTDOWN_DURATION: number = 5;
|
||||
/** 倒计时档位:快清场(压缩垃圾时间,奖励强 build) */
|
||||
private static readonly COUNTDOWN_FAST = 2.5;
|
||||
/** 倒计时档位:普通 */
|
||||
private static readonly COUNTDOWN_NORMAL = 4.0;
|
||||
/** 倒计时档位:有英雄死亡(保留满运营窗口:调整阵型/复活/买卡) */
|
||||
private static readonly COUNTDOWN_FULL = 5.0;
|
||||
/** 上一次显示的时间字符串(避免重复设置) */
|
||||
private lastTimeStr: string = "";
|
||||
/** 上一次显示的秒数(避免重复计算) */
|
||||
@@ -160,6 +164,12 @@ export class MissionComp extends CCComp {
|
||||
private currentWave: number = 0;
|
||||
/** 是否为Boss回合 */
|
||||
private isBossWave: boolean = false;
|
||||
/** 超时回合已在 onBattleTimeout 预扣留存分,BattleEnd 需跳过重复累加 */
|
||||
private skipRemainScoreOnBattleEnd: boolean = false;
|
||||
/** 上回合英雄死亡数快照(BattleEnd 写入,供下一回合倒计时档位判定) */
|
||||
private lastWaveDeathCount: number = 0;
|
||||
/** 上回合计时口径清场时间快照(含清场加速还原,供下一回合倒计时档位判定) */
|
||||
private lastWaveClearTime: number = 0;
|
||||
/** 当前任务阶段 */
|
||||
public currentPhase: MissionPhase = MissionPhase.None;
|
||||
/** 是否处于回合间倒计时状态 */
|
||||
@@ -248,6 +258,12 @@ export class MissionComp extends CCComp {
|
||||
smc.vmdata.mission_data.fight_time += dt
|
||||
this.clearTime += dt
|
||||
this.update_time();
|
||||
|
||||
// 回合超时兜底:僵局(坦克 vs 高血 Boss 互磨不死)时强制收束回合,Boss 回合阈值放宽
|
||||
const timeout = this.isBossWave ? WAVE_TIMEOUT_BOSS : WAVE_TIMEOUT;
|
||||
if (this.clearTime >= timeout) {
|
||||
this.onBattleTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,13 +291,20 @@ export class MissionComp extends CCComp {
|
||||
|
||||
// ======================== 回合倒计时 ========================
|
||||
|
||||
/** 进入回合间倒计时:重置倒计时并显示提示 */
|
||||
/** 进入回合间倒计时:按上回合战况分档(快清场压缩垃圾时间,有死亡保留运营窗口) */
|
||||
private startWaveCountdown() {
|
||||
this.waveCountdown = this.WAVE_COUNTDOWN_DURATION;
|
||||
this.waveCountdown = this.computeCountdown();
|
||||
this.isWaveCountdown = true;
|
||||
this.updateCountdownUI(true);
|
||||
}
|
||||
|
||||
/** 计算下一回合倒计时档位:读 BattleEnd 写入的快照(onNewWave 会清零 clearTime,不能现读) */
|
||||
private computeCountdown(): number {
|
||||
if (this.lastWaveDeathCount > 0) return MissionComp.COUNTDOWN_FULL;
|
||||
if (this.lastWaveClearTime > 0 && this.lastWaveClearTime < WAVE_DURATION * 0.65) return MissionComp.COUNTDOWN_FAST;
|
||||
return MissionComp.COUNTDOWN_NORMAL;
|
||||
}
|
||||
|
||||
/** 更新倒计时 UI(显示剩余秒数) */
|
||||
private updateCountdownUI(force: boolean = false) {
|
||||
if (!this.isWaveCountdown) return;
|
||||
@@ -491,12 +514,21 @@ export class MissionComp extends CCComp {
|
||||
|
||||
case MissionPhase.PrepareEnd:
|
||||
// 不隐藏开始按钮
|
||||
// 开闸刷怪:MissionMonComp 在本阶段启动分批释放,需解除暂停标志让第一批按时出场
|
||||
smc.mission.stop_spawn_mon = false;
|
||||
oops.message.dispatchEvent("PhasePrepareEnd");
|
||||
break;
|
||||
|
||||
case MissionPhase.BattleStart:
|
||||
// 触发战斗开始技能(fstart)
|
||||
this.triggerHeroBattleSkills(true);
|
||||
// Boss 回合预警:战斗正式开始、Boss 压轴进场(最后一批),提前给 UI 表现窗口
|
||||
if (this.isBossWave) {
|
||||
oops.message.dispatchEvent(GameEvent.BossWarning, {
|
||||
wave: this.currentWave,
|
||||
eta: BATCH_INTERVAL * (BATCH_COUNT - 1),
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case MissionPhase.Battle:
|
||||
@@ -520,7 +552,11 @@ export class MissionComp extends CCComp {
|
||||
// 【评分系统 - 战绩分】每回合胜利加分
|
||||
smc.vmdata.scores.wave_win_count++;
|
||||
// 【评分系统 - 战绩分】记录每回合结束时场上留存的敌人数量(扣分项)
|
||||
smc.vmdata.scores.wave_remain_monsters += smc.vmdata.mission_data.mon_num;
|
||||
// 超时回合已在 onBattleTimeout 预扣,跳过防重
|
||||
if (!this.skipRemainScoreOnBattleEnd) {
|
||||
smc.vmdata.scores.wave_remain_monsters += smc.vmdata.mission_data.mon_num;
|
||||
}
|
||||
this.skipRemainScoreOnBattleEnd = false;
|
||||
|
||||
let allAlive = true;
|
||||
let hasHero = false;
|
||||
@@ -537,8 +573,15 @@ export class MissionComp extends CCComp {
|
||||
});
|
||||
|
||||
// 【动态难度调节】根据本回合战况自动放水 / 加压
|
||||
DynamicTuner.adjust(this.clearTime, heroDeathCount);
|
||||
mLogger.log(this.debugMode, 'MissionComp', `[DynamicTuner] wave=${this.currentWave} clearTime=${this.clearTime.toFixed(1)}s deaths=${heroDeathCount} factor=${DynamicTuner.factor.toFixed(2)}`);
|
||||
// 清场加速节省的时间还原进口径,避免"打得好"被节奏加速+强度加压双重惩罚
|
||||
const effectiveClearTime = this.clearTime + smc.vmdata.mission_data.wave_early_skip;
|
||||
const tuned = DynamicTuner.adjust(effectiveClearTime, heroDeathCount);
|
||||
if (tuned) {
|
||||
mLogger.log(this.debugMode, 'MissionComp', `[DynamicTuner] wave=${this.currentWave} effClear=${effectiveClearTime.toFixed(1)}s deaths=${heroDeathCount} factor=${DynamicTuner.factor.toFixed(3)}`);
|
||||
}
|
||||
// 快照供下一回合倒计时档位判定(onNewWave 会清零 clearTime,必须提前快照)
|
||||
this.lastWaveDeathCount = heroDeathCount;
|
||||
this.lastWaveClearTime = effectiveClearTime;
|
||||
// 【评分系统 - 战绩分】记录全员存活的胜利回合数(额外加分)
|
||||
if (hasHero && allAlive) {
|
||||
smc.vmdata.scores.wave_all_alive_count++;
|
||||
@@ -553,7 +596,7 @@ export class MissionComp extends CCComp {
|
||||
// 触发战斗结束技能(fend)
|
||||
this.triggerHeroBattleSkills(false);
|
||||
|
||||
// 战斗结束阶段,给予所有英雄恢复70%血量的技能效果
|
||||
// 战斗结束阶段,按 FightSet.WAVE_HEAL_RATE 恢复所有英雄血量
|
||||
this.healAllHeroes();
|
||||
|
||||
// 【新增】派发每回合战斗结束事件,供卡牌技能监听(区别于整局结束的 MissionEnd)
|
||||
@@ -674,7 +717,7 @@ export class MissionComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 战斗结束阶段治疗所有英雄(包括墓地英雄),恢复70%最大生命值
|
||||
* 战斗结束阶段治疗所有英雄(包括墓地英雄),按 FightSet.WAVE_HEAL_RATE 恢复最大生命值比例
|
||||
*/
|
||||
private healAllHeroes() {
|
||||
const healRateBoost = FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.WaveHeal);
|
||||
@@ -788,6 +831,9 @@ export class MissionComp extends CCComp {
|
||||
this.currentPhase = MissionPhase.None;
|
||||
this.currentWave = 1;
|
||||
this.isBossWave = false;
|
||||
this.skipRemainScoreOnBattleEnd = false;
|
||||
this.lastWaveDeathCount = 0;
|
||||
this.lastWaveClearTime = 0;
|
||||
this.rewards = []
|
||||
this.revive_times = 1;
|
||||
this.lastTimeStr = "";
|
||||
@@ -901,6 +947,16 @@ export class MissionComp extends CCComp {
|
||||
// 20 回合通关
|
||||
this.open_Victory(null, false);
|
||||
} else {
|
||||
// oops.message 全局事件总线:清屏庆祝(横幅/奖励由 BattleBannerComp 消费),需在推进回合前派发(clearTime 归零前取值)
|
||||
const allAlive = this.checkAllHeroAlive();
|
||||
const fastClear = this.clearTime < WAVE_DURATION * 0.5 ? 2
|
||||
: this.clearTime < WAVE_DURATION * 0.75 ? 1 : 0;
|
||||
oops.message.dispatchEvent(GameEvent.WaveClear, {
|
||||
wave: this.currentWave,
|
||||
clearTime: this.clearTime,
|
||||
allAlive,
|
||||
fastClear,
|
||||
});
|
||||
oops.message.dispatchEvent("TimeUpAdvanceWave");
|
||||
}
|
||||
return;
|
||||
@@ -919,6 +975,20 @@ export class MissionComp extends CCComp {
|
||||
if (monsterCount >= max) smc.mission.stop_spawn_mon = true;
|
||||
}
|
||||
|
||||
/** 检测场上英雄是否全员存活(清屏 Perfect 判定用,独立于评分统计逻辑) */
|
||||
private checkAllHeroAlive(): boolean {
|
||||
let hasHero = false;
|
||||
let allAlive = true;
|
||||
ecs.query(this.heroAttrsMatcher).forEach(entity => {
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
if (attrs && attrs.fac === FacSet.HERO) {
|
||||
hasHero = true;
|
||||
if (attrs.is_dead) allAlive = false;
|
||||
}
|
||||
});
|
||||
return hasHero && allAlive;
|
||||
}
|
||||
|
||||
/**
|
||||
* 英雄全灭检测:若场上无存活英雄且处于战斗中,触发结算弹窗。
|
||||
* @param heroCount 当前存活英雄数量
|
||||
@@ -930,6 +1000,36 @@ export class MissionComp extends CCComp {
|
||||
this.open_Victory(null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回合超时强制结束(僵局兜底):
|
||||
* 1. 预扣留存分(残留怪销毁后 mon_num 会被下一次同步清零,BattleEnd 的累加会漏扣,故在此显式预扣并置防重标志)。
|
||||
* 2. 销毁全部残留怪(不触发 MonDead、不掉金币——超时是对"清不掉"的惩罚)。
|
||||
* 3. 复用正常回合推进流;DynamicTuner 因 clearTime 远超阈值会自动放水,无需特殊处理。
|
||||
*/
|
||||
private onBattleTimeout() {
|
||||
// 防重入:同一回合只触发一次
|
||||
if (this.currentPhase !== MissionPhase.Battle) return;
|
||||
|
||||
smc.vmdata.scores.wave_remain_monsters += smc.vmdata.mission_data.mon_num;
|
||||
this.skipRemainScoreOnBattleEnd = true;
|
||||
|
||||
ecs.query(this.heroAttrsMatcher).forEach(entity => {
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
if (attrs && attrs.fac === FacSet.MON && !attrs.is_dead) {
|
||||
entity.destroy();
|
||||
}
|
||||
});
|
||||
smc.vmdata.mission_data.mon_num = 0;
|
||||
|
||||
mLogger.log(this.debugMode, 'MissionComp', `[WaveTimeout] wave=${this.currentWave} 超时强制结束`);
|
||||
|
||||
if (this.currentWave >= MAX_WAVE) {
|
||||
this.open_Victory(null, false);
|
||||
} else {
|
||||
oops.message.dispatchEvent("TimeUpAdvanceWave");
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 清理 ========================
|
||||
|
||||
/** 清理所有英雄和技能 ECS 实体 */
|
||||
|
||||
Reference in New Issue
Block a user