/** * 肉鸽模式配置脚本 - 增强版 (Wave System) * * 功能说明: * - 采用 15 个小波次(每分钟 1 波) * - 整合进 3 个大的节奏阶段:构筑期、磨合期、极限期 * - 废弃动态预算,使用确定性波次配置 * * @author 游戏开发团队 * @version 3.0 波次重构版 * @date 2025-10-19 */ import { HeroInfo } from "../common/config/heroSet"; import { mLogger } from "../common/Logger"; /** * 怪物类型枚举 */ export enum MonType { NORMAL = 0, // 普通怪物 ELITE = 1, // 精英怪物 BOSS = 2 // Boss怪物 } /** * 怪物配置接口 (用于生成实例) */ export interface IMonsConfig { uuid: number; // 怪物ID type: MonType; // 怪物类型 level: number; // 等级 position?: number; // 位置(可选) buffs?: any[]; // buff列表(可选) } /** * 怪物属性接口 */ export interface MonAttrs { hp: number; mp: number; ap: number; def: number; speed: number; exp?: number; gold?: number; } /** * 成长类型枚举 */ enum GrowthType { EXPONENTIAL = 1.15, // 指数级 - HP LINEAR = 1.05, // 线性 - AP LOGARITHMIC = 0.3 // 对数级 - Speed } /** * 刷怪权重接口 */ export interface SpawnWeight { uuid: number; weight: number; type?: MonType; // 默认为 NORMAL } /** * 波次配置接口 */ export interface WaveConfig { waveId: number; // 波次ID (1-15) name: string; // 波次名称 duration: number; // 持续时间 (秒),通常为60 spawnInterval: number; // 刷怪间隔 (秒) maxActive: number; // 同屏最大怪物数 weights: SpawnWeight[]; // 怪物权重池 } // 怪物ID映射 (方便阅读) const MON_IDS = { WARRIOR: 5201, // 战士 ASSASSIN: 5301, // 斥候 TANK: 5401, // 卫士 ARCHER: 5501, // 射手 BOMBER: 5601, // 自爆兵 SUMMONER: 5602, // 召唤师 HEALER: 5603, // 祭司 TOTEM: 5604, // 图腾师 BOSS: 5701 // 首领 }; /** * 全局波次配置表 (15波) */ export const RogueWaves: WaveConfig[] = [ // --- 第一阶段:构筑期 (0-5min) --- { waveId: 1, name: "热身", duration: 60, spawnInterval: 2.0, maxActive: 5, weights: [{ uuid: MON_IDS.WARRIOR, weight: 100 }] }, { waveId: 2, name: "加速", duration: 60, spawnInterval: 1.8, maxActive: 6, weights: [ { uuid: MON_IDS.WARRIOR, weight: 80 }, { uuid: MON_IDS.ASSASSIN, weight: 20 } ] }, { waveId: 3, name: "堆叠", duration: 60, spawnInterval: 1.6, maxActive: 7, weights: [ { uuid: MON_IDS.WARRIOR, weight: 60 }, { uuid: MON_IDS.ASSASSIN, weight: 40 } ] }, { waveId: 4, name: "硬度测试", duration: 60, spawnInterval: 1.5, maxActive: 8, weights: [ { uuid: MON_IDS.WARRIOR, weight: 50 }, { uuid: MON_IDS.ASSASSIN, weight: 30 }, { uuid: MON_IDS.TANK, weight: 20 } ] }, { waveId: 5, name: "精英首秀", duration: 60, spawnInterval: 1.5, maxActive: 8, weights: [ { uuid: MON_IDS.WARRIOR, weight: 40 }, { uuid: MON_IDS.ASSASSIN, weight: 40 }, { uuid: MON_IDS.TANK, weight: 20 } // 注意:第5分钟会触发固定事件刷精英怪,这里只配普通怪 ] }, // --- 第二阶段:磨合期 (5-10min) --- { waveId: 6, name: "远程威胁", duration: 60, spawnInterval: 1.4, maxActive: 10, weights: [ { uuid: MON_IDS.TANK, weight: 30 }, { uuid: MON_IDS.ARCHER, weight: 40 }, { uuid: MON_IDS.WARRIOR, weight: 30 } ] }, { waveId: 7, name: "铁桶阵", duration: 60, spawnInterval: 1.3, maxActive: 10, weights: [ { uuid: MON_IDS.TANK, weight: 50 }, { uuid: MON_IDS.ARCHER, weight: 50 } ] }, { waveId: 8, name: "续航干扰", duration: 60, spawnInterval: 1.2, maxActive: 12, weights: [ { uuid: MON_IDS.WARRIOR, weight: 30 }, { uuid: MON_IDS.TANK, weight: 20 }, { uuid: MON_IDS.ARCHER, weight: 30 }, { uuid: MON_IDS.HEALER, weight: 20 } ] }, { waveId: 9, name: "走位测试", duration: 60, spawnInterval: 1.2, maxActive: 12, weights: [ { uuid: MON_IDS.WARRIOR, weight: 40 }, { uuid: MON_IDS.BOMBER, weight: 30 }, // 自爆兵 { uuid: MON_IDS.ARCHER, weight: 30 } ] }, { waveId: 10, name: "中场Boss", duration: 60, spawnInterval: 5.0, maxActive: 3, // Boss战期间,只刷少量护卫,Boss由事件触发 weights: [ { uuid: MON_IDS.TANK, weight: 100 } ] }, // --- 第三阶段:极限期 (10-15min) --- { waveId: 11, name: "混乱开端", duration: 60, spawnInterval: 1.0, maxActive: 15, weights: [ { uuid: MON_IDS.SUMMONER, weight: 20 }, { uuid: MON_IDS.TOTEM, weight: 20 }, { uuid: MON_IDS.WARRIOR, weight: 30 }, { uuid: MON_IDS.ARCHER, weight: 30 } ] }, { waveId: 12, name: "全家桶", duration: 60, spawnInterval: 0.9, maxActive: 18, weights: [ { uuid: MON_IDS.WARRIOR, weight: 15 }, { uuid: MON_IDS.ASSASSIN, weight: 15 }, { uuid: MON_IDS.TANK, weight: 15 }, { uuid: MON_IDS.ARCHER, weight: 15 }, { uuid: MON_IDS.BOMBER, weight: 15 }, { uuid: MON_IDS.HEALER, weight: 10 }, { uuid: MON_IDS.SUMMONER, weight: 15 } ] }, { waveId: 13, name: "精英小队", duration: 60, spawnInterval: 1.0, maxActive: 15, weights: [ { uuid: MON_IDS.TANK, weight: 40 }, { uuid: MON_IDS.ARCHER, weight: 40 }, { uuid: MON_IDS.WARRIOR, weight: 20, type: MonType.ELITE } // 尝试混入精英 ] }, { waveId: 14, name: "绝地求生", duration: 60, spawnInterval: 0.6, maxActive: 20, weights: [ { uuid: MON_IDS.ASSASSIN, weight: 50 }, { uuid: MON_IDS.BOMBER, weight: 50 } ] }, { waveId: 15, name: "终局", duration: 60, spawnInterval: 3.0, maxActive: 5, // 最终Boss战,只刷少量精英护卫 weights: [ { uuid: MON_IDS.TANK, weight: 100, type: MonType.ELITE } ] } ]; // 精英怪和Boss刷新时间配置 (时间单位: 秒) // 注意:这里的时间点应与波次结束/开始对应 export const SpecialMonsterSchedule = [ { time: 4 * 60 + 50, uuid: MON_IDS.WARRIOR, type: MonType.ELITE, level: 5, desc: "5分钟前夕: 精英战士" }, { time: 9 * 60 + 55, uuid: MON_IDS.BOSS, type: MonType.BOSS, level: 15, desc: "10分钟: 兽人首领" }, { time: 14 * 60 + 55, uuid: MON_IDS.BOSS, type: MonType.BOSS, level: 30, desc: "15分钟: 最终Boss" } ]; /** * 获取当前时间的波次配置 * @param timeInSeconds 游戏时间 (秒) */ export function getCurrentWave(timeInSeconds: number): WaveConfig { const waveIndex = Math.min(Math.floor(timeInSeconds / 60), 14); return RogueWaves[waveIndex]; } /** * 怪物消耗点数配置 (用于经验/金币计算) */ export const MonsterCost: Record = { 5201: 1, // 兽人战士 (Warrior) 5301: 3, // 兽人斥候 (Assassin) 5401: 5, // 兽人卫士 (Tank) 5501: 4, // 兽人射手 (Remote) 5601: 10, // 兽人自爆兵 (Mechanic) 5602: 8, // 兽人召唤师 5603: 6, // 兽人祭司 (Healer) 5604: 6, // 兽人图腾师 5701: 50, // 兽人首领 (Elite/Boss) }; /** * 计算波次因子 * @param stage 当前波次 * @param timeInSeconds 游戏进行时间(秒) * @returns 波次因子 (0-1之间,15分钟时达到最大) */ function calculateWaveFactor(stage: number, timeInSeconds: number = 0): number { const MAX_GAME_TIME = 15 * 60; // 15分钟 = 900秒 const effectiveTime = timeInSeconds || (stage * 60); const factor = Math.min(effectiveTime / MAX_GAME_TIME, 1.0); return factor; } /** * 应用成长公式到基础属性 */ function applyGrowthFormula(baseStat: number, waveFactor: number, growthType: GrowthType): number { // 基础倍率:15分钟成长约 21 倍 (1 + 1.0 * 20) const TIME_SCALING = 20; const growthMultiplier = Math.pow(1 + waveFactor * TIME_SCALING, growthType); return Math.floor(baseStat * growthMultiplier); } /** * 获取怪物动态成长属性 * @param stage 当前波次 (这里复用为等级或忽略) * @param uuid 怪物ID * @param monType 怪物类型 * @param timeInSeconds 游戏进行时间(秒) * @returns 怪物属性 */ export function getMonAttr(stage: number, uuid: number, monType: MonType = MonType.NORMAL, timeInSeconds: number = 0): MonAttrs { const baseMonster = HeroInfo[uuid]; if (!baseMonster) { mLogger.warn(true, 'RogueConfig', `[RogueConfig] 未找到怪物ID: ${uuid}`); return { hp: 100, mp: 100, ap: 10, def: 0, speed: 100 }; } // 计算波次因子 const waveFactor = calculateWaveFactor(0, timeInSeconds); // 动态质量系数:初始 1.5倍 -> 15分钟 6.0倍 // 大幅降低初始强度(原固定5.0),随时间线性增强 const qualityRatio = 1.5 + (4.5 * waveFactor); // 根据怪物类型应用额外的倍率 let typeMultiplier = 1.0; if (monType === MonType.ELITE) { typeMultiplier = 2.0; // 精英怪2倍属性 } else if (monType === MonType.BOSS) { typeMultiplier = 5.0; // Boss 5倍属性 } // 应用不同的成长类型 (应用质量系数) const hp = applyGrowthFormula(baseMonster.hp, waveFactor, GrowthType.EXPONENTIAL) * typeMultiplier * qualityRatio; const ap = applyGrowthFormula(baseMonster.ap, waveFactor, GrowthType.LINEAR) * typeMultiplier * qualityRatio; const speed = applyGrowthFormula(baseMonster.speed, waveFactor, GrowthType.LOGARITHMIC); // MP和DEF使用线性成长 (应用质量系数) const mp = applyGrowthFormula(baseMonster.mp, waveFactor, GrowthType.LINEAR) * qualityRatio; const def = applyGrowthFormula(baseMonster.def, waveFactor, GrowthType.LINEAR) * typeMultiplier * qualityRatio; return { hp: Math.floor(hp), mp: Math.floor(mp), ap: Math.floor(ap), def: Math.floor(def), speed: Math.floor(speed) }; } /** * 无限等级经验配置 */ export function getLevelExp(level: number): number { const baseExp = 100; const growthFactor = 1.2; return Math.floor(baseExp * Math.pow(growthFactor, level - 1)); } /** * 计算怪物掉落金币 */ export function calculateMonsterGold(uuid: number, level: number, type: MonType): number { const cost = MonsterCost[uuid] || 1; let danger_ratio = 1 + cost * 0.1; let type_ratio = 1; if(type == MonType.BOSS) type_ratio = 10; else if(type == MonType.ELITE) type_ratio = 3; const baseGold = 10; let gold = Math.floor((baseGold * type_ratio * danger_ratio + level) * 8); return gold; } /** * 计算怪物经验值 */ export function calculateMonsterExp(uuid: number, level: number): number { const cost = MonsterCost[uuid] || 1; return Math.max(1, Math.floor(cost * 1.0 * Math.pow(1.15, level - 1) * 8)); }