refactor: 统一将游戏内的「波次」术语替换为「回合」

本次提交将全项目中所有与「波次」相关的术语、注释、配置描述、UI文本全部替换为「回合」,包括:
1.  技能卡牌、怪物刷新、经济系统的相关注释与变量名
2.  配置表中的波次相关常量与描述文本
3.  UI预制体中的波次显示文本
4.  所有相关的文档注释与接口说明

统一术语后,游戏的关卡流程描述将更符合常规肉鸽游戏的表达习惯,避免与「波次」字面含义混淆,提升代码可读性与团队协作一致性。
This commit is contained in:
panFD
2026-08-09 14:51:48 +08:00
parent 2907c214f1
commit c59ba45b5b
14 changed files with 622 additions and 618 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -88,7 +88,7 @@ export class SingletonModuleComp extends ecs.Comp {
hero_num: 0,//英雄数量
hero_max_num: FightSet.HERO_MAX_NUM,//英雄可召唤上限
hero_extend_max_num: FightSet.HERO_MAX_NUM + 1,//英雄可拓展上限
wave_time_num: 0,//波次时间
wave_time_num: 0,//回合时间
in_fight: false,
fight_time: 0,//战斗时间
level: 1,//关卡等级

View File

@@ -46,7 +46,7 @@ export enum CardTriggerType {
Interval = 2, // 定时循环:战斗中按 t_inv 间隔重复触发
Field = 3, // 驻场光环(俗称「装备」):被动生效,由 field 数组驱动,支持单卡多词条
FightStart = 4, // 战斗开始时触发
FightEnd = 5, // 战斗结束时触发(每结束)
FightEnd = 5, // 战斗结束时触发(每回合结束)
HeroDead = 6, // 场上己方英雄死亡时触发
HeroCall = 7, // 英雄上场时触发(主角召唤 + 技能召唤 + 复活)
}
@@ -59,7 +59,7 @@ export interface CardConfig {
weight: number
kind: CKind
card_lv: number // 卡牌等级(驱动 bg_node 颜色1=green 2=blue 3=purple 4=red 5=yellow
wave?: number // 针对技能卡:仅在指定波次(wave)才能抽到
wave?: number // 针对技能卡:仅在指定回合(wave)才能抽到
hero_lv?: number
base_card_lv?: number
@@ -71,7 +71,7 @@ export interface CardConfig {
is_inst?: boolean // 是否即时起效
t_times?: number // 触发次数
t_inv?: number // 触发间隔(秒)
keep_waves?: number // 维持的波次数(-1表示持续到战斗结束0或undefined表示仅本波次
keep_waves?: number // 维持的回合数(-1表示持续到战斗结束0或undefined表示仅本回合
overrides?: SkillOverrides // 技能参数覆写如自定义伤害ap、buff值、金币数等
field?: number[] // 装备词条数组驻场光环卡trigger_type=Field的属性词条支持单卡多词条如 [7002, 7004, 7005] 同时提供攻击/暴击/暴伤)
@@ -80,7 +80,7 @@ export interface CardConfig {
/**
* 事件型触发的全局次数上限(仅 FightStart/FightEnd/HeroDead/HeroCall 有效)
* 默认 Infinity达到上限后销毁节点
* 注意:与 t_times 语义不同——t_times 控制每内 Interval 的次数
* 注意:与 t_times 语义不同——t_times 控制每回合内 Interval 的次数
*/
trigger_limit?: number;
@@ -242,7 +242,7 @@ export const drawCardsByRule = (
})
}
// 如果传入了波次并且是技能卡,则根据 wave 过滤
// 如果传入了回合并且是技能卡,则根据 wave 过滤
if (options.wave !== undefined) {
pool = pool.filter(card => {
if (card.type === CardType.Skill) {

View File

@@ -23,9 +23,9 @@ export enum FacSet {
MON = 1,
}
export enum FightSet {
WAVE_COIN_BASE = 7, // 波次金币基础奖励
WAVE_COIN_GROW = 2, // 波次金币递增值
WAVE_COIN_MAX = 17, // 波次金币最大基础奖励
WAVE_COIN_BASE = 7, // 回合金币基础奖励
WAVE_COIN_GROW = 2, // 回合金币递增值
WAVE_COIN_MAX = 17, // 回合金币最大基础奖励
CRIT_DAMAGE = 50,//暴击伤害
MORE_RC = 10,//更多次数 广告获取的次数
HEARTPOS = -320,//基地位置
@@ -55,11 +55,11 @@ export enum FightSet {
}
/**
* 技能卡牌出现的波次配置(单一数据源)。
* 技能卡牌出现的回合配置(单一数据源)。
* 数组索引 i 对应卡牌档位 card_lv = i + 1
* - 第 1 个波次 → LV1 档(基础强度)
* - 第 2 个波次 → LV2 档(强度 ×2
* - 第 3 个波次 → LV3 档(强度 ×3
* - 第 1 个回合 → LV1 档(基础强度)
* - 第 2 个回合 → LV2 档(强度 ×2
* - 第 3 个回合 → LV3 档(强度 ×3
* 需与 CardSet.SkillCardData 的 wave 字段严格对齐,否则抽卡时池子为空。
*/
export const SKILL_CARD_WAVES: number[] = [1, 5, 8];

View File

@@ -29,7 +29,7 @@ interface SkillCardRaw {
is_inst: boolean;
t_times?: number; // 触发次数(未设置时按 is_inst 取默认值:即时=1定时=3
t_inv?: number;
keep_waves?: number; // 维持波次数(默认 0=仅本波次
keep_waves?: number; // 维持回合数(默认 0=仅本回合
trigger_type: CardTriggerType;
overrides?: SkillOverrides;
}

View File

@@ -317,7 +317,7 @@ export const SkillSet: Record<number, SkillConfig> = {
RType: RType.fixed, EType: EType.animationEnd, info: "召唤陨石范围攻击敌人,有概率击晕",
},
//============================= ====== 辅助技能 技能卡牌 1 技能 ====== ==========================
//============================= ====== 辅助技能 技能卡牌 1 回合技能 ====== ==========================
6301: {
uuid: 6301, name: "护盾", sp_name: "buff_wind", icon: "Stat_Defense", TGroup: TGroup.Self, readyAnm: "up_blue", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Shield, ap: 3, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
@@ -328,7 +328,7 @@ export const SkillSet: Record<number, SkillConfig> = {
DTType: DTType.single, kind: SkillKind.Heal, ap: 300, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
RType: RType.fixed, EType: EType.animationEnd, info: "治疗伙伴/自己",
},
//==========================buff 技能 也是 技能卡牌 5 技能
//==========================buff 技能 也是 技能卡牌 5 回合技能
6303: {
uuid: 6303, name: "获取金币", sp_name: "buff_wind", icon: "Stat_GoldGainIncrease_01", TGroup: TGroup.Self, readyAnm: "up_blue", endAnm: "gold", act: "atk",
DTType: DTType.single, kind: SkillKind.Gold, ap: 0, gold: 1, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,

View File

@@ -304,9 +304,9 @@ export interface LvBonusEntry {
* dead 战士/刺客 死亡触发,一次性全队遗产
* fstart 法师 战斗开始前触发一次,必然生效的全队增益
* field 法师 驻场光环,存活期间持续生效,死亡消失
* fend 辅助 每结束触发,跨波次累积增益
* fend 辅助 每回合结束触发,跨回合累积增益
*
* 触发节奏atked 3次/5次atking 5次/7次fstart/fend 1次/
* 触发节奏atked 3次/5次atking 5次/7次fstart/fend 1次/回合
*
* skills[0]是普通攻击技能
*/
@@ -724,7 +724,7 @@ export const HeroInfo: Record<number, heroInfo> = {
5051: {
uuid: 5051, name: "占卜师", path: "hm1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, hp: 143, ap: 40,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } },
// 战前计时 buff开始全队 AP 限时提升(+30/5s → +60/7s → +90/10s
// 战前计时 buff回合开始全队 AP 限时提升(+30/5s → +60/7s → +90/10s
// 统一复用 7111 基础配置,通过 buff_value/buff_duration 覆写实现三档成长
fstart: {
6401: [
@@ -810,7 +810,7 @@ export const HeroInfo: Record<number, heroInfo> = {
info: "<color=#27AE60>✧</color>光环全队提升攻击\n<color=#9B59B6>✦</color>追加提升攻速",
},
// ========== fend + atking 类(辅助 · 治疗续航 + 波次增益) ==========
// ========== fend + atking 类(辅助 · 治疗续航 + 回合增益) ==========
5071: {
uuid: 5071, name: "治愈牧师", path: "hh1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, hp: 143, ap: 40,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal3].cd, ccd: 0 } },
@@ -831,21 +831,21 @@ export const HeroInfo: Record<number, heroInfo> = {
// skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid1].cd, ccd: 0 } },
// atking: [{ s_uuid: 6302, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 200 } }],
// fend: [{ s_uuid: 6303, t_num: 1, overrides: { gold: 1 } }],
// info: "攻击5次治疗全队结束获得1金币"
// info: "攻击5次治疗全队回合结束获得1金币"
// },
// 5073: {
// uuid: 5073, name: "强化牧师", path: "hh3", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, hp: 430, ap: 120,
// skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal3].cd, ccd: 0 } },
// atking: [{ s_uuid: 6302, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 250 } }],
// fend: [{ s_uuid: 6401, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 10 } }],
// info: "攻击5次治疗全队结束全队永久+10攻击力"
// info: "攻击5次治疗全队回合结束全队永久+10攻击力"
// },
// 5074: {
// uuid: 5074, name: "生命牧师", path: "hh4", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, hp: 573, ap: 160,
// skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal3].cd, ccd: 0 } },
// atking: [{ s_uuid: 6302, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 250 } }],
// fend: [{ s_uuid: 6402, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 80 } }],
// info: "攻击5次治疗全队结束全队永久+80最大生命值"
// info: "攻击5次治疗全队回合结束全队永久+80最大生命值"
// },
// 5075: {
// uuid: 5075, name: "全能牧师", path: "hh5", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, hp: 717, ap: 200,
@@ -855,7 +855,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// { s_uuid: 6401, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 12 } },
// { s_uuid: 6402, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 60 } }
// ],
// info: "攻击5次治疗全队300%AP结束全队永久+12攻击力、+60最大生命值"
// info: "攻击5次治疗全队300%AP回合结束全队永久+12攻击力、+60最大生命值"
// },
@@ -959,7 +959,7 @@ export const HeroList: number[] = [
// 5053, 5054, 5055, 5056,
// field 类(法师 · 驻场光环)
5061,
// fend + atking 类(辅助 · 治疗 + 波次增益)
// fend + atking 类(辅助 · 治疗 + 回合增益)
5071,
// 5072, 5073, 5074, 5075,
];
@@ -971,7 +971,7 @@ export const HeroList: number[] = [
* - W_HP: 每 1 点生命值折算 1 分(生命值不随时间变化)
* - W_DPS_PER_SEC: 每 1 点 DPS 持续 1 秒折算 0.5 分
* (默认 60 秒评估窗下1 DPS ≈ 30 分,与旧版 W_DPS=30 等效)
* - WAVE_DURATION / HITS_PER_WAVE: 波次参数,用于估算 atked/fstart/fend/dead 触发次数
* - WAVE_DURATION / HITS_PER_WAVE: 回合参数,用于估算 atked/fstart/fend/dead 触发次数
* - DEFAULT_EVAL_SECONDS: calcHeroPower 默认使用的评估时间窗
*/
export const HERO_POWER_WEIGHTS = {
@@ -1086,7 +1086,7 @@ function getHitCount(seconds: number): number {
return (seconds / weights.WAVE_DURATION) * weights.HITS_PER_WAVE;
}
/** 计算目标时间窗内的波次数 */
/** 计算目标时间窗内的回合数 */
function getWaveCount(seconds: number): number {
return seconds / HERO_POWER_WEIGHTS.WAVE_DURATION;
}

View File

@@ -9,18 +9,18 @@
* 4. 触发结束后自动销毁。
*
* 触发类型CardTriggerType
* - Instant (1)init 时立即触发一次(按 t_times 控制次数,跨波次 NewWave 时再次触发)
* - Interval (2):监听 FightStart → update 帧驱动按 t_inv 间隔重复触发(按 t_times 控制每次数)
* - Instant (1)init 时立即触发一次(按 t_times 控制次数,跨回合 NewWave 时再次触发)
* - Interval (2):监听 FightStart → update 帧驱动按 t_inv 间隔重复触发(按 t_times 控制每回合次数)
* - Field (3):被动生效,不主动施法(实际由 FieldSkillSet 处理)
* - FightStart (4):监听 FightStart 事件,按 trigger_limit 全局累计上限
* - FightEnd (5):监听 FightEnd 事件(每结束派发),按 trigger_limit 全局累计上限
* - FightEnd (5):监听 FightEnd 事件(每回合结束派发),按 trigger_limit 全局累计上限
* - HeroDead (6):监听 HeroDead 事件(仅英雄阵营派发,怪物死亡不触发)
* - HeroCall (7):监听 MasterCalled主角/技能召唤)+ ReviveSuccess复活
*
* 关键设计:
* - 事件型4-7统一走 onEventTrigger 入口,仅作触发信号,不读取 payload
* - 触发上限Instant/Interval 按 t_times内),事件型按 trigger_limit全局
* - 跨波次keep_waves 控制存活;事件型 trigger_count 不随波次重置
* - 触发上限Instant/Interval 按 t_times回合内),事件型按 trigger_limit全局
* - 跨回合keep_waves 控制存活;事件型 trigger_count 不随回合重置
* - 销毁时通过 GameEvent.RemoveSkillBox 通知 MissSkillsComp 回收槽位
*
* 依赖:
@@ -89,7 +89,7 @@ export class EquipBoxComp extends CCComp {
private trigger_times: number = 1;
/** 触发间隔(秒,仅持续技能有效) */
private trigger_interval: number = 0;
/** 维持的波次数(-1表示直到战斗结束0表示不跨波次>0表示维持的具体波次数) */
/** 维持的回合数(-1表示直到战斗结束0表示不跨回合>0表示维持的具体回合数) */
private keep_waves: number = 0;
/** 技能覆写参数自定义伤害、Buff等 */
private overrides?: SkillOverrides;
@@ -123,7 +123,7 @@ export class EquipBoxComp extends CCComp {
/**
* 注册全局事件:
* - MissionEnd所有类型都需要监听任务结束时强制销毁
* - NewWave处理 keep_waves 跨波次逻辑(所有类型统一)
* - NewWave处理 keep_waves 跨回合逻辑(所有类型统一)
* - 其它触发事件由 registerTrigger 按 trigger_type 动态注册
*/
onLoad() {
@@ -354,14 +354,14 @@ export class EquipBoxComp extends CCComp {
this.current_trigger_times++;
this.updateUI();
// 单次触发 + 不跨波次维持 → 延迟销毁(保留短暂视觉反馈)
// 单次触发 + 不跨回合维持 → 延迟销毁(保留短暂视觉反馈)
if (this.keep_waves === 0 && this.current_trigger_times >= this.trigger_times) {
this.scheduleOnce(() => this.destroySelf(), 1.0);
}
return;
}
// 事件型:上限由 trigger_limit 控制(全局累计,跨波次不重置)
// 事件型:上限由 trigger_limit 控制(全局累计,跨回合不重置)
if (this.trigger_count >= this.trigger_limit) {
this.destroySelf();
return;
@@ -498,30 +498,30 @@ export class EquipBoxComp extends CCComp {
this.timer = 0; // 重置计时器
}
/** 节点级新一事件处理 */
/** 节点级新一回合事件处理 */
private onNewWave() {
this.handleNewWave();
}
/** 全局级新一事件处理 */
/** 全局级新一回合事件处理 */
private onNewWaveGlobal() {
this.handleNewWave();
}
/**
* 新一:退出战斗状态。
* 处理维持波次逻辑:递减剩余波次,或者重置触发次数。
* 新一回合:退出战斗状态。
* 处理维持回合逻辑:递减剩余回合,或者重置触发次数。
*
* 各类型在新一的行为:
* - Instant/Interval/FightStart/FightEnd按 keep_waves 决定维持/销毁,并在新一开始时重置本地计数
* 各类型在新一回合的行为:
* - Instant/Interval/FightStart/FightEnd按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
* - Field被动生效跟随 keep_waves 决定存活
* - HeroDead/HeroCall波次触发的事件型trigger_count全局不重置仅 keep_waves 控制存活
* - HeroDead/HeroCall回合触发的事件型trigger_count全局不重置仅 keep_waves 控制存活
*/
private handleNewWave() {
if (!this.initialized) return;
this.in_combat = false;
// 事件型触发HeroDead / HeroCalltrigger_count 全局累计,不随波次重置
// 事件型触发HeroDead / HeroCalltrigger_count 全局累计,不随回合重置
const isGlobalEventType =
this.trigger_type === CardTriggerType.HeroDead ||
this.trigger_type === CardTriggerType.HeroCall;
@@ -534,21 +534,21 @@ export class EquipBoxComp extends CCComp {
return;
}
}
// 跨波次维持:重置本地计数与计时器(事件型 trigger_count 不重置)
// 跨回合维持:重置本地计数与计时器(事件型 trigger_count 不重置)
if (!isGlobalEventType) {
this.current_trigger_times = 0;
this.trigger_count = 0;
}
this.timer = 0;
// 即时/事件型触发一次保持旧行为Instant 在新一开始立即触发一次)
// 即时/事件型触发一次保持旧行为Instant 在新一回合开始立即触发一次)
if (this.trigger_type === CardTriggerType.Instant) {
this.triggerSkill();
this.current_trigger_times++;
}
this.updateUI();
} else {
// 不跨波次维持:达到上限即销毁
// 不跨回合维持:达到上限即销毁
// - Interval / Instant按 t_times 判定
// - 事件型:按 trigger_limit 判定
const reachedLimit = isGlobalEventType
@@ -598,7 +598,7 @@ export class EquipBoxComp extends CCComp {
this.current_trigger_times++;
this.updateUI();
// 次数用完且不跨波次维持 → 延迟销毁
// 次数用完且不跨回合维持 → 延迟销毁
if (this.keep_waves === 0 && this.current_trigger_times >= this.trigger_times) {
this.scheduleOnce(() => this.destroySelf(), 0.5);
}

View File

@@ -23,7 +23,7 @@ const { ccclass, property } = _decorator;
* MSkillBoxComp —— 技能卡牌系统核心控制器
*
* 管理 3 个卡牌槽位的抽卡分发、卡池升级、金币费用、
* 在 1,5,10,15,20 波次弹出技能卡牌三选一
* 在 1,5,10,15,20 回合弹出技能卡牌三选一
*/
@ccclass('MSkillBoxComp')
@ecs.register('MissionCard', false)

View File

@@ -6,7 +6,7 @@
* 1. 管理单局游戏的 **完整生命周期**:初始化 → 准备阶段 → 战斗阶段 → 结算。
* 2. 在战斗阶段每帧更新战斗计时器、同步怪物数量、检测英雄全灭。
* 3. 管理怪物数量阈值(暂停 / 恢复刷怪的上下限)。
* 4. 处理新一事件NewWave进入准备阶段并发放金币奖励。
* 4. 处理新一回合事件NewWave进入准备阶段并发放金币奖励。
* 5. 提供战斗结束后的结算弹窗入口VictoryComp
* 6. (可选)内建性能监控面板,显示内存、帧率、实体数量等开发信息。
*
@@ -22,7 +22,7 @@
*
* 依赖:
* - smc.mission —— 全局任务运行状态play / pause / in_fight / stop_spawn_mon 等)
* - smc.vmdata.mission_data —— 局内数据(金币 / 数 / 怪物数量等)
* - smc.vmdata.mission_data —— 局内数据(金币 / 回合数 / 怪物数量等)
* - FightSet —— 战斗常量配置
* - CardInitCoins —— 初始金币数
* - UIID.Victory —— 结算弹窗
@@ -92,7 +92,7 @@ export class MissionComp extends CCComp {
/** 开始战斗按钮 */
@property(Node)
start_btn: Node = null!
/** 时间/数显示节点 */
/** 时间/回合数显示节点 */
@property(Node)
time_node: Node = null!
@property(Node)
@@ -112,7 +112,7 @@ export class MissionComp extends CCComp {
// ======================== 运行时状态 ========================
/** 本战斗已耗时(秒),正向计时,用于自适应难度评估 */
/** 本回合战斗已耗时(秒),正向计时,用于自适应难度评估 */
clearTime: number = 0
/** 剩余复活次数 */
revive_times: number = 1;
@@ -126,9 +126,9 @@ export class MissionComp extends CCComp {
}
/**秒计时 */
PhaseTime: Timer = new Timer(1)
/** 波次间倒计时(秒) */
/** 回合间倒计时(秒) */
private waveCountdown: number = 0;
/** 波次间倒计时总时长(秒) */
/** 回合间倒计时总时长(秒) */
private readonly WAVE_COUNTDOWN_DURATION: number = 5;
/** 上一次显示的时间字符串(避免重复设置) */
private lastTimeStr: string = "";
@@ -156,13 +156,13 @@ export class MissionComp extends CCComp {
private heapTrendBaseMB: number = -1;
/** 怪物数量同步计时器(降低同步频率) */
private monsterCountSyncTimer: number = 0;
/** 当前数 */
/** 当前回合数 */
private currentWave: number = 0;
/** 是否为Boss波次 */
/** 是否为Boss回合 */
private isBossWave: boolean = false;
/** 当前任务阶段 */
public currentPhase: MissionPhase = MissionPhase.None;
/** 是否处于波次间倒计时状态 */
/** 是否处于回合间倒计时状态 */
private isWaveCountdown: boolean = false;
// ======================== ECS 查询匹配器(预缓存) ========================
@@ -186,7 +186,7 @@ export class MissionComp extends CCComp {
this.on(GameEvent.NewWave, this.onNewWave, this)
this.on(GameEvent.DO_AD_BACK, this.do_ad, this)
this.start_btn?.on(NodeEventType.TOUCH_END, this.onStartFightBtnClick, this)
// 第一准备阶段,首个英雄召唤后自动开始战斗(无需玩家点击开始按钮)
// 第一回合准备阶段,首个英雄召唤后自动开始战斗(无需玩家点击开始按钮)
oops.message.on(GameEvent.MasterCalled, this.onHeroSummoned, this);
this.removeMemoryPanel()
}
@@ -223,7 +223,7 @@ export class MissionComp extends CCComp {
// 如果是暂停状态,且不在 BattleEnd 阶段(全灭时需要播放完 fend 技能动画并自动流转),才真正停止 update 逻辑
if (smc.mission.pause && this.currentPhase !== MissionPhase.BattleEnd) return
// 波次间倒计时PrepareStart 复用为倒计时阶段5 秒后自动进入下一
// 回合间倒计时PrepareStart 复用为倒计时阶段5 秒后自动进入下一回合
if (this.currentPhase === MissionPhase.PrepareStart) {
this.waveCountdown -= dt;
this.updateCountdownUI();
@@ -253,7 +253,7 @@ export class MissionComp extends CCComp {
// ======================== 时间显示 ========================
/** 更新时间/数显示(仅在秒数变化时更新以减少 Label 操作) */
/** 更新时间/回合数显示(仅在秒数变化时更新以减少 Label 操作) */
update_time() {
const remainSecond = Math.floor(smc.vmdata.mission_data.fight_time);
if (remainSecond === this.lastTimeSecond) return;
@@ -273,9 +273,9 @@ export class MissionComp extends CCComp {
}
}
// ======================== 波次倒计时 ========================
// ======================== 回合倒计时 ========================
/** 进入波次间倒计时:重置倒计时并显示提示 */
/** 进入回合间倒计时:重置倒计时并显示提示 */
private startWaveCountdown() {
this.waveCountdown = this.WAVE_COUNTDOWN_DURATION;
this.isWaveCountdown = true;
@@ -294,13 +294,13 @@ export class MissionComp extends CCComp {
if (phaseNode) {
const label = phaseNode.getComponent(Label);
if (label) {
label.string = `下一 ${remain}s`;
label.string = `下一回合 ${remain}s`;
}
}
}
}
/** 结束波次间倒计时 */
/** 结束回合间倒计时 */
private stopWaveCountdown() {
this.isWaveCountdown = false;
this.lastTimeSecond = -1;
@@ -446,7 +446,7 @@ export class MissionComp extends CCComp {
const label = phaseNode.getComponent(Label);
if (label) {
const wave = Math.max(1, this.currentWave || (smc.vmdata && smc.vmdata.mission_data ? smc.vmdata.mission_data.level : 1) || 1);
label.string = `${wave} `;
label.string = `${wave} 回合`;
}
// 阶段切换动感表现:只在进入战斗阶段跳动一下,让流程充满心流体验
@@ -471,12 +471,12 @@ export class MissionComp extends CCComp {
smc.mission.in_fight = false;
smc.vmdata.mission_data.in_fight = false;
smc.mission.stop_spawn_mon = true;
// 波次间倒计时隐藏开始按钮nobg 激活表示不可点击状态)
// 回合间倒计时隐藏开始按钮nobg 激活表示不可点击状态)
if (this.start_btn && this.start_btn.isValid) {
const nobg = this.start_btn.getChildByName("nobg");
if (nobg) nobg.active = true;
}
// 启动 5 秒倒计时,由 update 驱动自动进入下一
// 启动 5 秒倒计时,由 update 驱动自动进入下一回合
this.startWaveCountdown();
oops.message.dispatchEvent("PhasePrepareStart");
break;
@@ -536,7 +536,7 @@ 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)}`);
// 【评分系统 - 战绩分】记录全员存活的胜利回合数(额外加分)
@@ -556,7 +556,7 @@ export class MissionComp extends CCComp {
// 战斗结束阶段给予所有英雄恢复70%血量的技能效果
this.healAllHeroes();
// 【新增】派发每战斗结束事件,供卡牌技能监听(区别于整局结束的 MissionEnd
// 【新增】派发每回合战斗结束事件,供卡牌技能监听(区别于整局结束的 MissionEnd
oops.message.dispatchEvent(GameEvent.FightEnd);
break;
@@ -586,7 +586,7 @@ export class MissionComp extends CCComp {
private autoNextPhase() {
switch (this.currentPhase) {
case MissionPhase.PrepareStart:
// 波次间倒计时结束,停止倒计时状态,直接进入 PrepareEnd不再等待玩家点击
// 回合间倒计时结束,停止倒计时状态,直接进入 PrepareEnd不再等待玩家点击
this.stopWaveCountdown();
this.changePhase(MissionPhase.PrepareEnd);
break;
@@ -599,7 +599,7 @@ export class MissionComp extends CCComp {
case MissionPhase.BattleEnd:
// BattleEnd 计时结束后,如果是因为全灭或手动调用的 fight_end进入 Settle
// 需要注意的是open_Victory / fight_end 现在只需切换到 BattleEnd 即可Settle 由这里自动接管
// 如果游戏正在运行(波次更迭),则自动进入 PrepareStart 阶段
// 如果游戏正在运行(回合更迭),则自动进入 PrepareStart 阶段
if (smc.mission.play && !smc.mission.pause) {
this.changePhase(MissionPhase.PrepareStart);
} else {
@@ -712,9 +712,9 @@ export class MissionComp extends CCComp {
/**
* 英雄召唤事件回调:
* 第一准备阶段Prepare只有第 1 才会进入,
* 第一回合准备阶段Prepare只有第 1 回合才会进入,
* 此时首个英雄一旦召唤即自动开战,无需玩家点击开始按钮。
* 后续波次走 PrepareStart 倒计时自动流转,不会进入 Prepare。
* 后续回合走 PrepareStart 倒计时自动流转,不会进入 Prepare。
*/
private onHeroSummoned() {
if (this.currentPhase !== MissionPhase.Prepare) return;
@@ -732,7 +732,7 @@ export class MissionComp extends CCComp {
* @param is_hero_dead 是否因英雄全灭触发
*/
open_Victory(e: any, is_hero_dead: boolean = false) {
// 战斗失败或胜利,标记暂停状态以切断波次流转逻辑
// 战斗失败或胜利,标记暂停状态以切断回合流转逻辑
smc.mission.pause = true;
// 直接切入 BattleEnd触发 fend 表现
// 倒计时逻辑已在 update 中由 PhaseTime 接管2s 后将触发 autoNextPhase 弹窗结算
@@ -767,7 +767,7 @@ export class MissionComp extends CCComp {
/**
* 初始化全部局内数据:
* - 全局运行标志
* - 战斗时间 / 怪物数量 / 金币 /
* - 战斗时间 / 怪物数量 / 金币 / 回合
* - 奖励列表 / 复活次数
* - 性能监控基准值
*/
@@ -816,15 +816,15 @@ export class MissionComp extends CCComp {
smc.vmdata.scores.gold_earned += smc.vmdata.mission_data.coin;
}
// ======================== 波次管理 ========================
// ======================== 回合管理 ========================
/**
* 新一事件回调:
* 新一回合事件回调:
* 1. 进入准备阶段。
* 2. 更新当前数。
* 2. 更新当前回合数。
* 3. 刷新时间显示。
*
* 注意:金币不再按波次固定发放,改为怪物死亡时掉落(见 HeroAtkSystem
* 注意:金币不再按回合固定发放,改为怪物死亡时掉落(见 HeroAtkSystem
*
* @param event 事件名
* @param data { wave: number }
@@ -836,20 +836,20 @@ export class MissionComp extends CCComp {
this.isBossWave = !!data?.bossWave;
if (wave > 1) {
// 在新一到来时,先进入 BattleEnd触发上一的战斗结束技能 (fend)2秒后自动进入下一的准备阶段
// 在新一回合到来时,先进入 BattleEnd触发上一回合的战斗结束技能 (fend)2秒后自动进入下一回合的准备阶段
this.changePhase(MissionPhase.BattleEnd);
} else {
// 第1不需要结束上一延迟一点播放提示避免被开始游戏的loading遮挡
// 第1回合不需要结束上一回合延迟一点播放提示避免被开始游戏的loading遮挡
this.scheduleOnce(() => {
if (this.currentPhase === MissionPhase.Prepare) {
this.playTooltipAnim(`${wave} `);
this.playTooltipAnim(`${wave} 回合`);
}
}, 0.5);
}
this.currentWave = wave;
smc.vmdata.mission_data.level = wave;
// 金币改为怪物死亡掉落(见 HeroAtkSystem不再每固定发放
// 金币改为怪物死亡掉落(见 HeroAtkSystem不再每回合固定发放
this.lastTimeSecond = -1;
this.clearTime = 0;
this.update_time();
@@ -894,11 +894,11 @@ export class MissionComp extends CCComp {
});
this.handleHeroWipe(heroCount);
// 怪物全灭检测:如果战斗阶段场上没有任何活着的怪物,且待刷新的怪物队列也为空,直接结束战斗进入下一的准备阶段
// 怪物全灭检测:如果战斗阶段场上没有任何活着的怪物,且待刷新的怪物队列也为空,直接结束战斗进入下一回合的准备阶段
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 (this.currentWave >= MAX_WAVE) {
// 20 通关
// 20 回合通关
this.open_Victory(null, false);
} else {
oops.message.dispatchEvent("TimeUpAdvanceWave");

View File

@@ -149,7 +149,7 @@ export class MissionEconomy {
}
/**
* 计算每基础金币收益
* 计算每回合基础金币收益
*/
static getWaveGold(baseReward: number): number {
let reward = Math.max(0, Math.floor(baseReward));
@@ -159,7 +159,7 @@ export class MissionEconomy {
}
/**
* 执行发放每金币
* 执行发放每回合金币
*/
static executeWaveGold(baseReward: number): number {
const reward = this.getWaveGold(baseReward);

View File

@@ -1,17 +1,17 @@
/**
* @file MissionMonComp.ts
* @description 怪物Monster波次刷新管理组件(逻辑层)
* @description 怪物Monster回合刷新管理组件(逻辑层)
*
* 职责:
* 1. 管理每一怪物的生成计划:根据 RogueConfig 生成怪物。
* 2. 自动推进波次在准备阶段结束时PhasePrepareEnd启动分批释放。
* 3. 分批刷怪:每固定 3 批,每 10 秒释放一批,批内按 MON_SPAWN_INTERVAL 逐个刷出。
* 1. 管理每一回合怪物的生成计划:根据 RogueConfig 生成怪物。
* 2. 自动推进回合在准备阶段结束时PhasePrepareEnd启动分批释放。
* 3. 分批刷怪:每回合固定 3 批,每 10 秒释放一批,批内按 MON_SPAWN_INTERVAL 逐个刷出。
*
* 关键设计:
* - 所有怪物统一从右侧 X=400 出生点逐个刷出MON_SPAWN_INTERVAL 节奏控制)。
* - 实际阵型与推进由 MonMoveComp 在战斗中向左移动自然形成,不再使用固定网格点。
* - 槽位索引3行×4列仅用于 monGrid 寻路与 SCastSystem 索敌定位。
* - 上一残留怪在波次结束/开始时统一清理。
* - 上一回合残留怪在回合结束/开始时统一清理。
*/
import { _decorator, v3, Vec3 } from "cc";
import { mLogger } from "../common/Logger";
@@ -44,7 +44,7 @@ export class MissionMonCompComp extends CCComp {
* 怪物占位点:所有槽位统一以右侧出生点 (X=400) 作为坐标,
* 实际阵型由 MonMoveComp 在战斗中向左推进时自然拉开。
* 槽位索引仍保留 3 行 × 4 列结构,供 monGrid 寻路与 SCastSystem 索敌使用。
* 超出 12 个槽位的怪物(放松)按 spawnIndex % 12 复用槽位。
* 超出 12 个槽位的怪物(放松回合)按 spawnIndex % 12 复用槽位。
*/
public static readonly MON_POSITIONS: Vec3[] = [
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 0: Col1-Top
@@ -70,11 +70,11 @@ export class MissionMonCompComp extends CCComp {
/** 全局生成顺序计数器(用于渲染层级排序) */
private globalSpawnOrder: number = 0;
/** 当前数 */
/** 当前回合数 */
private currentWave: number = 0;
/** 当前的目标怪物总数 */
/** 当前回合的目标怪物总数 */
private waveTargetCount: number = 0;
/** 当前已生成的怪物数量 */
/** 当前回合已生成的怪物数量 */
private waveSpawnedCount: number = 0;
/** 等待生成的怪物队列按批次分组batch 0~2 */
private pendingBatches: GeneratedMonster[][] = [[], [], []];
@@ -142,7 +142,7 @@ export class MissionMonCompComp extends CCComp {
let hasBoss = monsters.some(m => m.isBoss);
mLogger.log(this.debugMode, 'MissionMonComp', `[MissionMonComp] 波次 ${this.currentWave} 生成怪物总数: ${this.waveTargetCount}`);
mLogger.log(this.debugMode, 'MissionMonComp', `[MissionMonComp] 回合 ${this.currentWave} 生成怪物总数: ${this.waveTargetCount}`);
oops.message.dispatchEvent(GameEvent.NewWave, {
wave: this.currentWave,
@@ -152,7 +152,7 @@ export class MissionMonCompComp extends CCComp {
}
/**
* 战斗准备:重置所有运行时状态并开始第一
* 战斗准备:重置所有运行时状态并开始第一回合
*/
fight_ready() {
smc.vmdata.mission_data.mon_num = 0;
@@ -168,7 +168,7 @@ export class MissionMonCompComp extends CCComp {
this.spawnQueue = [];
this.spawnTimer = 0;
// 预生成第一数据以获取数量和 Boss 信息
// 预生成第一回合数据以获取数量和 Boss 信息
const monsters = spawningEngine.generateWave(this.currentWave);
this.setupWaveData(monsters);
@@ -179,10 +179,10 @@ export class MissionMonCompComp extends CCComp {
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System");
}
// ======================== 波次管理 ========================
// ======================== 回合管理 ========================
/**
* 开始下一波:波数 +1 并预生成数据
* 开始下一回合:回合数 +1 并预生成数据
*/
private onTimeUpAdvanceWave() {
this.currentWave += 1;
@@ -247,7 +247,7 @@ export class MissionMonCompComp extends CCComp {
// ======================== 槽位管理 ========================
/**
* 清理上一残留怪物,并重置生成计数与逐个刷怪队列
* 清理上一回合残留怪物,并重置生成计数与逐个刷怪队列
*/
private resetSlotSpawnData() {
ecs.query(ecs.allOf(HeroAttrsComp)).forEach(e => {
@@ -258,7 +258,7 @@ export class MissionMonCompComp extends CCComp {
});
this.waveSpawnedCount = 0;
// 同步丢弃上一未释放的队列,避免与新一混合
// 同步丢弃上一回合未释放的队列,避免与新一回合混合
this.spawnQueue = [];
this.spawnTimer = 0;
this.isReleasing = false;

View File

@@ -3,24 +3,24 @@
* @description 肉鸽刷怪系统(基于英雄强度的动态难度 + 心流循环)
*
* 设计层次:
* 1. WaveType - 型(普通 / 压力 / 放松5 一个心流循环
* 2. WaveConfig - 每硬编码(数量 + 小队池 + HP/AP 倍率 + 强度微调)
* 1. WaveType - 回合类型(普通 / 压力 / 放松5 回合一个心流循环
* 2. WaveConfig - 每回合硬编码(数量 + 小队池 + HP/AP 倍率 + 强度微调)
* 3. DynamicTuner - 动态难度调节器(系统根据战况放水 / 加压)
* 4. MonSkillSet - 怪物技能池atking / atked / dead 等全触发类型)
* 5. RogueSpawningEngine - 生成引擎(按英雄强度反推怪物强度)
*
* 核心公式:
* heroPower = Σ calcHeroPower(HeroInfo[uuid], lv) (场上存活英雄)
* targetPower = heroPower × 型系数 × wave.power_adjust × DynamicTuner.factor
* targetPower = heroPower × 回合类型系数 × wave.power_adjust × DynamicTuner.factor
* scale = targetPower ÷ Σ 怪物基础强度
* 每只怪: hp ×= scale, ap ×= scale
*
* 波次节奏:
* - 最大 20 ,第 20 通关
* - 每 30 秒,固定分 3 批,每 10 秒释放一批
* - 普通 18~36 只,放松 × 1.5 = 27~54 只
* - wave % 5 === 0 → 压力(必带 Boss强度高、数量少
* - wave % 5 === 1 → 放松(数量 × 1.5,强度低,爽快清屏)
* 回合节奏:
* - 最大 20 回合,第 20 回合通关
* - 每回合 30 秒,固定分 3 批,每 10 秒释放一批
* - 普通回合 18~36 只,放松回合 × 1.5 = 27~54 只
* - wave % 5 === 0 → 压力回合(必带 Boss强度高、数量少
* - wave % 5 === 1 → 放松回合(数量 × 1.5,强度低,爽快清屏)
*/
import { HeroInfo, MonType, MonTypeName, calcHeroPower, TriggerGrouped, LvReviveEntry, heroInfo } from "../common/config/heroSet";
@@ -29,50 +29,50 @@ import { FacSet } from "../common/config/GameSet";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
// ======================== 1. 型与节奏常量 ========================
// ======================== 1. 回合类型与节奏常量 ========================
/** 型枚举 */
/** 回合类型枚举 */
export enum WaveType {
Normal = 0, // 普通
Pressure = 1, // 压力wave % 5 === 0必带 Boss
Relax = 2, // 放松wave % 5 === 1量大强度低
Normal = 0, // 普通回合
Pressure = 1, // 压力回合wave % 5 === 0必带 Boss
Relax = 2, // 放松回合wave % 5 === 1量大强度低
}
/** 型名称 */
/** 回合类型名称 */
export const WaveTypeName: Record<WaveType, string> = {
[WaveType.Normal]: "普通",
[WaveType.Pressure]: "压力",
[WaveType.Relax]: "放松",
};
/** 型强度系数(硬编码) */
/** 回合类型强度系数(硬编码) */
export const WAVE_TYPE_POWER_RATIO: Record<WaveType, number> = {
[WaveType.Normal]: 0.9, // 普通:标准强度
[WaveType.Pressure]: 1.2, // 压力:强度高、数量少
[WaveType.Relax]: 0.6, // 放松:量大、强度低
[WaveType.Normal]: 0.9, // 普通回合:标准强度
[WaveType.Pressure]: 1.2, // 压力回合:强度高、数量少
[WaveType.Relax]: 0.6, // 放松回合:量大、强度低
};
/** 放松数量倍率(相对普通 */
/** 放松回合数量倍率(相对普通回合 */
export const RELAX_COUNT_MUL = 1.5;
/** 最大波次(第 20 通关) */
/** 最大回合数(第 20 回合通关) */
export const MAX_WAVE = 20;
/** 每时长(秒) */
/** 每回合时长(秒) */
export const WAVE_DURATION = 30;
/** 每分批次数 */
/** 每回合分批次数 */
export const BATCH_COUNT = 3;
/** 每批间隔30 秒 / 3 批 = 10 秒 */
export const BATCH_INTERVAL = WAVE_DURATION / BATCH_COUNT;
/** 每怪物硬上限(放松 36 × 1.5 = 54 */
/** 每回合怪物硬上限(放松回合 36 × 1.5 = 54 */
export const MAX_MONSTERS = 54;
/**
* 获取指定波次的波
* @param wave 波次1 起)
* 获取指定回合的回合类
* @param wave 回合1 起)
* @returns WaveType
*/
export function getWaveType(wave: number): WaveType {
@@ -87,7 +87,7 @@ export function getWaveType(wave: number): WaveType {
* 动态难度调节器(系统控制放水 / 加压)
* 与硬编码系数并存,用于根据战况实时微调难度。
*
* 用法示例MissionComp 每结束时调用):
* 用法示例MissionComp 每回合结束时调用):
* DynamicTuner.adjust(clearTime, heroDeathCount);
*
* 调节规则(内部硬编码):
@@ -107,7 +107,7 @@ export const DynamicTuner = {
STEP: 0.05,
/**
* 根据上一战况自动调节难度
* 根据上一回合战况自动调节难度
* @param clearTime 清场耗时(秒)
* @param heroDeathCount 英雄死亡数
*/
@@ -228,7 +228,7 @@ export interface MonSkillSet {
/**
* 怪物技能池(硬编码)
* 压力 / Boss 可配置专属技能,普通随机挂载增加变数。
* 压力回合 / Boss 可配置专属技能,普通回合随机挂载增加变数。
*
* 技能 uuid 引用 SkillSet 中的 6000~6500 段触发技能。
*/
@@ -270,7 +270,7 @@ export const MonSkillPool: Record<string, MonSkillSet> = {
},
};
/** Boss 专属技能池(压力 Boss 随机挂载) */
/** Boss 专属技能池(压力回合 Boss 随机挂载) */
export const BossSkillPool: Record<string, MonSkillSet> = {
/** 狂暴领主:攻击触发全队攻击提升 */
boss_rage: {
@@ -301,37 +301,37 @@ export const BossSkillPool: Record<string, MonSkillSet> = {
},
};
// ======================== 6. 波次配置表 ========================
// ======================== 6. 回合配置表 ========================
/** 单波次完整配置 */
/** 单回合完整配置 */
export interface WaveConfig {
/** 基础怪物总数(普通 36 为上限,放松自动 × 1.5 = 54 */
/** 基础怪物总数(普通回合 36 为上限,放松回合自动 × 1.5 = 54 */
base_count: number;
/** 可选小队 id 池,引擎按权重抽取拼装到 base_count */
squad_pool: string[];
/** HP 强化倍率(硬编码,逐递进) */
/** HP 强化倍率(硬编码,逐回合递进) */
hp_mul: number;
/** AP 强化倍率(硬编码,逐递进) */
/** AP 强化倍率(硬编码,逐回合递进) */
ap_mul: number;
/** 强度微调(放水 / 加压,默认 1.0 */
power_adjust?: number;
/** 是否 Boss (压力必为 true */
/** 是否 Boss 回合(压力回合必为 true */
boss_wave?: boolean;
/** 普通怪技能池 id可选随机挂载 */
skill_pool?: string[];
/** Boss 技能池 idBoss 专用,随机挂载) */
/** Boss 技能池 idBoss 回合专用,随机挂载) */
boss_skill_pool?: string[];
}
/**
* 波次配置表(硬编码 wave 1~20
* 回合配置表(硬编码 wave 1~20
*
* 心流循环5 一循环):
* wave % 5 === 0 → 压力(必带 Boss强度高、数量少
* wave % 5 === 1 → 放松(数量 × 1.5,强度低)
* 其余 → 普通(标准强度)
* 心流循环5 回合一循环):
* wave % 5 === 0 → 压力回合(必带 Boss强度高、数量少
* wave % 5 === 1 → 放松回合(数量 × 1.5,强度低)
* 其余 → 普通回合(标准强度)
*
* 强度递进hp_mul / ap_mul 每 5 一档,压力额外提升。
* 强度递进hp_mul / ap_mul 每 5 回合一档,压力回合额外提升。
*/
export const WaveConfigs: Record<number, WaveConfig> = {
// ===== 第一循环:教学期 =====
@@ -339,34 +339,34 @@ export const WaveConfigs: Record<number, WaveConfig> = {
2: { base_count: 21, squad_pool: ["melee_grunt", "mixed_balanced"], hp_mul: 1.00, ap_mul: 1.00 },
3: { base_count: 24, squad_pool: ["melee_grunt", "mixed_balanced", "long_line"], hp_mul: 1.05, ap_mul: 1.00 },
4: { base_count: 27, squad_pool: ["mixed_balanced", "long_line", "heavy_shield"], hp_mul: 1.10, ap_mul: 1.05 },
// 压力:第一 Boss
// 压力回合:第一 Boss
5: { base_count: 21, squad_pool: ["melee_grunt", "mixed_balanced", "heavy_shield"], hp_mul: 1.15, ap_mul: 1.10, boss_wave: true, boss_skill_pool: ["boss_rage"] },
// ===== 第二循环:引入技能怪 =====
// 放松:量大好清
// 放松回合:量大好清
6: { base_count: 30, squad_pool: ["melee_grunt", "mixed_balanced", "long_line"], hp_mul: 1.10, ap_mul: 1.05 },
7: { base_count: 30, squad_pool: ["melee_grunt", "heavy_shield", "long_line"], hp_mul: 1.15, ap_mul: 1.10, skill_pool: ["tough"] },
8: { base_count: 33, squad_pool: ["assassin_squad", "mixed_balanced", "summoner_cult"], hp_mul: 1.20, ap_mul: 1.15, skill_pool: ["berserk"] },
9: { base_count: 33, squad_pool: ["heavy_shield", "long_line", "mixed_balanced"], hp_mul: 1.25, ap_mul: 1.20, skill_pool: ["berserk", "tough"] },
// 压力:第二 Boss
// 压力回合:第二 Boss
10: { base_count: 24, squad_pool: ["melee_grunt", "assassin_squad", "mixed_balanced"], hp_mul: 1.30, ap_mul: 1.25, boss_wave: true, boss_skill_pool: ["boss_rage", "boss_iron"] },
// ===== 第三循环:组合多样化 =====
// 放松
// 放松回合
11: { base_count: 33, squad_pool: ["assassin_squad", "long_line", "summoner_cult", "mixed_balanced"], hp_mul: 1.25, ap_mul: 1.20, skill_pool: ["leech"] },
12: { base_count: 33, squad_pool: ["heavy_shield", "melee_grunt", "mixed_balanced", "long_line"], hp_mul: 1.30, ap_mul: 1.25, skill_pool: ["tough", "warcry"] },
13: { base_count: 36, squad_pool: ["assassin_squad", "summoner_cult", "mixed_balanced"], hp_mul: 1.35, ap_mul: 1.30, skill_pool: ["berserk", "leech"] },
14: { base_count: 36, squad_pool: ["heavy_shield", "long_line", "melee_grunt"], hp_mul: 1.40, ap_mul: 1.35, skill_pool: ["berserk", "tough", "legacy"] },
// 压力:第三 Boss中期高潮
// 压力回合:第三 Boss中期高潮
15: { base_count: 27, squad_pool: ["assassin_squad", "mixed_balanced", "summoner_cult"], hp_mul: 1.50, ap_mul: 1.40, boss_wave: true, boss_skill_pool: ["boss_rage", "boss_iron", "boss_doom"] },
// ===== 第四循环:终极阶段 =====
// 放松
// 放松回合
16: { base_count: 36, squad_pool: ["assassin_squad", "heavy_shield", "long_line"], hp_mul: 1.45, ap_mul: 1.40, skill_pool: ["leech", "warcry"] },
17: { base_count: 36, squad_pool: ["melee_grunt", "summoner_cult", "mixed_balanced"], hp_mul: 1.50, ap_mul: 1.45, skill_pool: ["berserk", "legacy"] },
18: { base_count: 36, squad_pool: ["assassin_squad", "long_line", "heavy_shield"], hp_mul: 1.55, ap_mul: 1.50, skill_pool: ["berserk", "tough", "leech"] },
19: { base_count: 36, squad_pool: ["mixed_balanced", "summoner_cult", "melee_grunt"], hp_mul: 1.60, ap_mul: 1.55, skill_pool: ["berserk", "tough", "legacy", "warcry"] },
// 压力:最终 Boss
// 压力回合:最终 Boss
20: { base_count: 30, squad_pool: ["assassin_squad", "heavy_shield", "summoner_cult", "mixed_balanced"], hp_mul: 1.80, ap_mul: 1.70, boss_wave: true, boss_skill_pool: ["boss_doom", "boss_rage"] },
};
@@ -472,13 +472,13 @@ export const TestModeConfig = {
/**
* 肉鸽刷怪生成引擎
* 按英雄强度反推怪物强度,结合型系数与动态调节器生成怪物列表
* 按英雄强度反推怪物强度,结合回合类型系数与动态调节器生成怪物列表
*/
export class RogueSpawningEngine {
/**
* 生成指定波次的怪物列表
* @param waveNumber 波次1 起,>MAX_WAVE 时复用 wave MAX_WAVE 配置)
* 生成指定回合的怪物列表
* @param waveNumber 回合1 起,>MAX_WAVE 时复用 wave MAX_WAVE 配置)
* @returns 怪物列表,长度 ≤ MAX_MONSTERS
*/
generateWave(waveNumber: number): GeneratedMonster[] {
@@ -499,14 +499,14 @@ export class RogueSpawningEngine {
const powerAdjust = cfg.power_adjust ?? 1.0;
const targetPower = heroPower * typeRatio * powerAdjust * DynamicTuner.factor;
// 2. 确定怪物总数(放松 × 1.5
// 2. 确定怪物总数(放松回合 × 1.5
let totalCount = cfg.base_count;
if (waveType === WaveType.Relax) {
totalCount = Math.round(totalCount * RELAX_COUNT_MUL);
}
totalCount = Math.min(totalCount, MAX_MONSTERS);
// 3. Boss 位(压力必带 Boss占 1 个名额)
// 3. Boss 位(压力回合必带 Boss占 1 个名额)
const monsters: GeneratedMonster[] = [];
let remaining = totalCount;
if (cfg.boss_wave) {
@@ -563,8 +563,8 @@ export class RogueSpawningEngine {
}
/**
* 获取指定波次的怪物总数
* @param waveNumber 目标
* 获取指定回合的怪物总数
* @param waveNumber 目标回合
* @returns 预计生成的怪物总数
*/
getWaveMonsterCount(waveNumber: number): number {
@@ -672,7 +672,7 @@ export class RogueSpawningEngine {
const baseHp = baseInfo ? baseInfo.hp : 500;
const baseAp = baseInfo ? baseInfo.ap : 30;
// Boss 自带额外血量加成:每 5 递增 20%wave 5=1.0, 10=1.2, 15=1.4...
// Boss 自带额外血量加成:每 5 回合递增 20%wave 5=1.0, 10=1.2, 15=1.4...
const bossBonusHpMul = 1 + 0.20 * Math.floor(Math.max(0, wave - 5) / 5);
return {
@@ -753,21 +753,21 @@ export class RogueSpawningEngine {
export const spawningEngine = new RogueSpawningEngine();
/** 历史接口:波次槽位(保留兼容) */
/** 历史接口:回合槽位(保留兼容) */
export interface IWaveSlot {
type: number;
count: number;
}
/**
* 获取指定波次预计的怪物总数量
* @param waveNumber 目标
* 获取指定回合预计的怪物总数量
* @param waveNumber 目标回合
*/
export function getWaveMonsterCount(waveNumber: number): number {
return spawningEngine.getWaveMonsterCount(waveNumber);
}
/** 获取波次槽位配置(向后兼容,从 generateWave 反推) */
/** 获取回合槽位配置(向后兼容,从 generateWave 反推) */
export function getWaveSlotConfig(waveNumber: number): IWaveSlot[] {
const generated = spawningEngine.generateWave(waveNumber);
const slotMap = new Map<number, number>();
@@ -785,7 +785,7 @@ export const DefaultWaveSlot: IWaveSlot[] = [
{ type: MonType.Support, count: 5 },
];
/** 波次槽位配置代理(按需生成) */
/** 回合槽位配置代理(按需生成) */
export const WaveSlotConfig: { [wave: number]: IWaveSlot[] } = new Proxy(
{} as { [wave: number]: IWaveSlot[] },
{

View File

@@ -9,18 +9,18 @@
* 4. 触发结束后自动销毁。
*
* 触发类型CardTriggerType
* - Instant (1)init 时立即触发一次(按 t_times 控制次数,跨波次 NewWave 时再次触发)
* - Interval (2):监听 FightStart → update 帧驱动按 t_inv 间隔重复触发(按 t_times 控制每次数)
* - Instant (1)init 时立即触发一次(按 t_times 控制次数,跨回合 NewWave 时再次触发)
* - Interval (2):监听 FightStart → update 帧驱动按 t_inv 间隔重复触发(按 t_times 控制每回合次数)
* - Field (3):被动生效,不主动施法(实际由 FieldSkillSet 处理)
* - FightStart (4):监听 FightStart 事件,按 trigger_limit 全局累计上限
* - FightEnd (5):监听 FightEnd 事件(每结束派发),按 trigger_limit 全局累计上限
* - FightEnd (5):监听 FightEnd 事件(每回合结束派发),按 trigger_limit 全局累计上限
* - HeroDead (6):监听 HeroDead 事件(仅英雄阵营派发,怪物死亡不触发)
* - HeroCall (7):监听 MasterCalled主角/技能召唤)+ ReviveSuccess复活
*
* 关键设计:
* - 事件型4-7统一走 onEventTrigger 入口,仅作触发信号,不读取 payload
* - 触发上限Instant/Interval 按 t_times内),事件型按 trigger_limit全局
* - 跨波次keep_waves 控制存活;事件型 trigger_count 不随波次重置
* - 触发上限Instant/Interval 按 t_times回合内),事件型按 trigger_limit全局
* - 跨回合keep_waves 控制存活;事件型 trigger_count 不随回合重置
* - 销毁时通过 GameEvent.RemoveSkillBox 通知 MissSkillsComp 回收槽位
*
* 依赖:
@@ -89,7 +89,7 @@ export class SkillBoxComp extends CCComp {
private trigger_times: number = 1;
/** 触发间隔(秒,仅持续技能有效) */
private trigger_interval: number = 0;
/** 维持的波次数(-1表示直到战斗结束0表示不跨波次>0表示维持的具体波次数) */
/** 维持的回合数(-1表示直到战斗结束0表示不跨回合>0表示维持的具体回合数) */
private keep_waves: number = 0;
/** 技能覆写参数自定义伤害、Buff等 */
private overrides?: SkillOverrides;
@@ -123,7 +123,7 @@ export class SkillBoxComp extends CCComp {
/**
* 注册全局事件:
* - MissionEnd所有类型都需要监听任务结束时强制销毁
* - NewWave处理 keep_waves 跨波次逻辑(所有类型统一)
* - NewWave处理 keep_waves 跨回合逻辑(所有类型统一)
* - 其它触发事件由 registerTrigger 按 trigger_type 动态注册
*/
onLoad() {
@@ -304,7 +304,7 @@ export class SkillBoxComp extends CCComp {
case CardTriggerType.Interval:
// 定时循环:监听 FightStart 进入战斗后启动计时
oops.message.on(GameEvent.FightStart, this.onFightStart, this);
// 若购买时战斗已在进行FightStart 已派发过),立即进入战斗态,避免空等下一
// 若购买时战斗已在进行FightStart 已派发过),立即进入战斗态,避免空等下一回合
if (smc.mission.in_fight) {
this.in_combat = true;
this.timer = 0;
@@ -361,14 +361,14 @@ export class SkillBoxComp extends CCComp {
this.current_trigger_times++;
this.updateUI();
// 单次触发 + 不跨波次维持 → 延迟销毁(保留短暂视觉反馈)
// 单次触发 + 不跨回合维持 → 延迟销毁(保留短暂视觉反馈)
if (this.keep_waves === 0 && this.current_trigger_times >= this.trigger_times) {
this.scheduleOnce(() => this.destroySelf(), 1.0);
}
return;
}
// 事件型:上限由 trigger_limit 控制(全局累计,跨波次不重置)
// 事件型:上限由 trigger_limit 控制(全局累计,跨回合不重置)
if (this.trigger_count >= this.trigger_limit) {
this.destroySelf();
return;
@@ -504,30 +504,30 @@ export class SkillBoxComp extends CCComp {
this.timer = 0; // 重置计时器
}
/** 节点级新一事件处理 */
/** 节点级新一回合事件处理 */
private onNewWave() {
this.handleNewWave();
}
/** 全局级新一事件处理 */
/** 全局级新一回合事件处理 */
private onNewWaveGlobal() {
this.handleNewWave();
}
/**
* 新一:退出战斗状态。
* 处理维持波次逻辑:递减剩余波次,或者重置触发次数。
* 新一回合:退出战斗状态。
* 处理维持回合逻辑:递减剩余回合,或者重置触发次数。
*
* 各类型在新一的行为:
* - Instant/Interval/FightStart/FightEnd按 keep_waves 决定维持/销毁,并在新一开始时重置本地计数
* 各类型在新一回合的行为:
* - Instant/Interval/FightStart/FightEnd按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
* - Field被动生效跟随 keep_waves 决定存活
* - HeroDead/HeroCall波次触发的事件型trigger_count全局不重置仅 keep_waves 控制存活
* - HeroDead/HeroCall回合触发的事件型trigger_count全局不重置仅 keep_waves 控制存活
*/
private handleNewWave() {
if (!this.initialized) return;
this.in_combat = false;
// 事件型触发HeroDead / HeroCalltrigger_count 全局累计,不随波次重置
// 事件型触发HeroDead / HeroCalltrigger_count 全局累计,不随回合重置
const isGlobalEventType =
this.trigger_type === CardTriggerType.HeroDead ||
this.trigger_type === CardTriggerType.HeroCall;
@@ -540,21 +540,21 @@ export class SkillBoxComp extends CCComp {
return;
}
}
// 跨波次维持:重置本地计数与计时器(事件型 trigger_count 不重置)
// 跨回合维持:重置本地计数与计时器(事件型 trigger_count 不重置)
if (!isGlobalEventType) {
this.current_trigger_times = 0;
this.trigger_count = 0;
}
this.timer = 0;
// 即时/事件型触发一次保持旧行为Instant 在新一开始立即触发一次)
// 即时/事件型触发一次保持旧行为Instant 在新一回合开始立即触发一次)
if (this.trigger_type === CardTriggerType.Instant) {
this.triggerSkill();
this.current_trigger_times++;
}
this.updateUI();
} else {
// 不跨波次维持:达到上限即销毁
// 不跨回合维持:达到上限即销毁
// - Interval / Instant按 t_times 判定
// - 事件型:按 trigger_limit 判定
const reachedLimit = isGlobalEventType
@@ -604,7 +604,7 @@ export class SkillBoxComp extends CCComp {
this.current_trigger_times++;
this.updateUI();
// 次数用完且不跨波次维持 → 延迟销毁
// 次数用完且不跨回合维持 → 延迟销毁
if (this.keep_waves === 0 && this.current_trigger_times >= this.trigger_times) {
this.scheduleOnce(() => this.destroySelf(), 0.5);
}