docs: 更新肉鸽刷怪系统文档与配置至15波5阶梯版本

同步调整了文档中的波次、阶梯、词缀、预算等全部数值配置,同时更新了RogueConfig.ts中的代码配置,将原30波10阶梯架构重构为15波5阶梯版本,包括调整怪物生成模板、属性倍率、词缀解锁等级和无限模式起始阶梯等核心参数。
This commit is contained in:
walkpan
2026-05-15 20:27:36 +08:00
parent af5b79b9a1
commit 9687adb559
2 changed files with 181 additions and 196 deletions

View File

@@ -3,7 +3,7 @@
* @description 肉鸽刷怪系统 v2 —— 三层程序化生成架构
*
* 架构:蓝图模板(节奏) + 权重填充(内容) + 自适应微调(数值)
* 主线 30 波 / 10 阶梯 / 每档 3 波(恢复→攀升→高潮)
* 主线 15 波 / 5 阶梯 / 每档 3 波(恢复→攀升→高潮)
* 10 种怪物 + 8 种词缀 + 自适应难度 ±15%
* 通关后可选无限模式(分层推进)
*
@@ -86,47 +86,47 @@ export interface AffixConfig {
export const AffixConfigs: Record<AffixType, AffixConfig> = {
[AffixType.Elite]: {
name: "精英", hpMultiplier: 1.5, apMultiplier: 1.3,
cost: 20, tierMin: 5, description: "+50% HP, +30% AP",
cost: 20, tierMin: 3, description: "+50% HP, +30% AP",
},
[AffixType.Berserk]: {
name: "狂暴", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 15, tierMin: 5, description: "攻速 ×1.5 (行为层实现)",
cost: 15, tierMin: 3, description: "攻速 ×1.5 (行为层实现)",
},
[AffixType.Shield]: {
name: "护盾", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 25, tierMin: 6, description: "开局带 抵御2次 伤害吸收盾",
cost: 25, tierMin: 3, description: "开局带 抵御2次 伤害吸收盾",
},
[AffixType.Regen]: {
name: "再生", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 20, tierMin: 7, description: "每秒回复 2% HP",
cost: 20, tierMin: 4, description: "每秒回复 2% HP",
},
[AffixType.Swift]: {
name: "疾速", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 10, tierMin: 7, description: "移速 ×2",
cost: 10, tierMin: 4, description: "移速 ×2",
},
[AffixType.Giant]: {
name: "巨型", hpMultiplier: 2.0, apMultiplier: 1.5,
cost: 30, tierMin: 8, description: "×2 体型, +100% HP, +50% AP",
cost: 30, tierMin: 4, description: "×2 体型, +100% HP, +50% AP",
},
[AffixType.Chain]: {
name: "连锁", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 20, tierMin: 9, description: "攻击附带 50% 溅射伤害",
cost: 20, tierMin: 5, description: "攻击附带 50% 溅射伤害",
},
[AffixType.SummonerA]: {
name: "召唤", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 25, tierMin: 10, description: "每 8 秒召唤 1 个小怪",
cost: 25, tierMin: 5, description: "每 8 秒召唤 1 个小怪",
},
[AffixType.CritRes]: {
name: "坚韧", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 15, tierMin: 6, description: "+暴击抗性",
cost: 15, tierMin: 3, description: "+暴击抗性",
},
[AffixType.FreezeRes]: {
name: "防寒", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 15, tierMin: 6, description: "+冰冻抗性",
cost: 15, tierMin: 3, description: "+冰冻抗性",
},
[AffixType.KnockbackRes]: {
name: "稳固", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 15, tierMin: 6, description: "+击退抗性",
cost: 15, tierMin: 3, description: "+击退抗性",
},
}
@@ -250,38 +250,29 @@ export interface TierConfig {
}
/** Boss 出现的 Tier 集合MiniBoss 或 MajorBoss */
const BOSS_TIERS = new Set([2, 4, 5, 7, 9, 10])
const BOSS_TIERS = new Set([1, 2, 3, 4, 5])
/** MajorBoss高难度 Boss出现的 Tier 集合 */
const MAJOR_BOSS_TIERS = new Set([5, 10])
const MAJOR_BOSS_TIERS = new Set([3, 5])
/**
* 10 阶梯配置表
* key = 阶梯编号 1-10value = 该阶梯的完整配置
* 主线 30 波映射wave 1-3 → T1, wave 4-6 → T2, ..., wave 28-30 → T10
* 5 阶梯配置表
* key = 阶梯编号 1-5value = 该阶梯的完整配置
* 主线 15 波映射wave 1-3 → T1, wave 4-6 → T2, ..., wave 13-15 → T5
*/
export const TierConfigs: Record<number, TierConfig> = {
1: { multiplier: 1.0, budget: 100, availableTypes: [MonType.Melee], isBossTier: false },
2: { multiplier: 1.3, budget: 150, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long], isBossTier: true },
3: { multiplier: 1.6, budget: 200, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long], isBossTier: false },
4: { multiplier: 1.9, budget: 260, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support], isBossTier: true },
5: { multiplier: 2.3, budget: 340, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber], isBossTier: true },
6: { multiplier: 2.8, budget: 440, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin], isBossTier: false },
7: { multiplier: 3.3, budget: 560, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: true },
8: { multiplier: 3.9, budget: 700, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: false },
9: { multiplier: 4.6, budget: 860, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: true },
10: { multiplier: 5.5, budget: 1050, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: true },
1: { multiplier: 1.0, budget: 500, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long], isBossTier: false },
2: { multiplier: 1.6, budget: 1000, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support], isBossTier: true },
3: { multiplier: 2.5, budget: 1800, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin], isBossTier: true },
4: { multiplier: 3.8, budget: 3000, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: true },
5: { multiplier: 5.5, budget: 5000, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: true },
}
/**
* 获取指定阶梯的配置
* 支持 Tier 1-10(查表)和 Tier 11+无限模式递推计算T(n) = T(n-1) × 1.2
* 支持 Tier 1-5(查表)和 Tier 6+无限模式递推计算T(n) = T(n-1) × 1.2
*
* @param tier - 阶梯编号1-10 主线,11+ 无限模式)
* @param tier - 阶梯编号1-5 主线,6+ 无限模式)
* @returns TierConfig 该阶梯的完整配置
*
* @example
* getTierConfig(1) // { multiplier: 1.0, budget: 100, ... }
* getTierConfig(11) // { multiplier: 6.6, budget: 1260, ... } (5.5 × 1.2)
*/
export function getTierConfig(tier: number): TierConfig {
if (TierConfigs[tier]) return TierConfigs[tier]
@@ -305,13 +296,12 @@ export function getTierConfig(tier: number): TierConfig {
/**
* 每个 Tier 的基础词缀触发概率0.0-1.0
* T1-T4: 0%教学期无词缀)
* T5: 10%, T6: 15%, T7: 25%, T8: 30%, T9: 40%, T10: 50%
* T1-T2: 0%期无词缀)
* T3: 15%, T4: 30%, T5: 50%
* 最终概率 = baseAffixChance × roleMultiplier
*/
export const BaseAffixChance: Record<number, number> = {
1: 0, 2: 0, 3: 0, 4: 0,
5: 0.10, 6: 0.15, 7: 0.25, 8: 0.30, 9: 0.40, 10: 0.50,
1: 0, 2: 0, 3: 0.15, 4: 0.30, 5: 0.50,
}
/**
@@ -381,100 +371,100 @@ export interface BlueprintTemplate {
export const BlueprintTemplates: BlueprintTemplate[] = [
// ---- REST 类 ----
{ id: "R1", type: TemplateType.REST, tierMin: 1, allowAffix: false,
slots: [{ typePool: [MonType.Melee], countMin: 1, countMax: 2, weight: 1.0 }] },
{ id: "R2", type: TemplateType.REST, tierMin: 2, allowAffix: false,
slots: [{ typePool: [MonType.Melee, MonType.Heavy], countMin: 1, countMax: 3, weight: 1.0 }] },
{ id: "R3", type: TemplateType.REST, tierMin: 5, allowAffix: false,
slots: [{ typePool: [MonType.Melee, MonType.Long], countMin: 2, countMax: 3, weight: 1.0 }] },
slots: [{ typePool: [MonType.Melee], countMin: 5, countMax: 10, weight: 1.0 }] },
{ id: "R2", type: TemplateType.REST, tierMin: 1, allowAffix: false,
slots: [{ typePool: [MonType.Melee, MonType.Heavy], countMin: 5, countMax: 15, weight: 1.0 }] },
{ id: "R3", type: TemplateType.REST, tierMin: 3, allowAffix: false,
slots: [{ typePool: [MonType.Melee, MonType.Long], countMin: 10, countMax: 15, weight: 1.0 }] },
// ---- NORMAL 类 ----
{ id: "N1", type: TemplateType.NORMAL, tierMin: 1, allowAffix: false,
slots: [{ typePool: [MonType.Melee], countMin: 2, countMax: 4, weight: 1.0 }] },
{ id: "N2", type: TemplateType.NORMAL, tierMin: 2, allowAffix: false,
slots: [{ typePool: [MonType.Melee], countMin: 10, countMax: 20, weight: 1.0 }] },
{ id: "N2", type: TemplateType.NORMAL, tierMin: 1, allowAffix: false,
slots: [
{ typePool: [MonType.Melee], countMin: 1, countMax: 3, weight: 0.6 },
{ typePool: [MonType.Long, MonType.Heavy], countMin: 1, countMax: 2, weight: 0.4 },
{ typePool: [MonType.Melee], countMin: 5, countMax: 15, weight: 0.6 },
{ typePool: [MonType.Long, MonType.Heavy], countMin: 5, countMax: 10, weight: 0.4 },
] },
{ id: "N3", type: TemplateType.NORMAL, tierMin: 4, allowAffix: true,
{ id: "N3", type: TemplateType.NORMAL, tierMin: 2, allowAffix: true,
slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 3, weight: 0.5 },
{ typePool: [MonType.Long], countMin: 1, countMax: 2, weight: 0.3 },
{ typePool: [MonType.Support], countMin: 1, countMax: 1, weight: 0.2 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 15, weight: 0.5 },
{ typePool: [MonType.Long], countMin: 5, countMax: 10, weight: 0.3 },
{ typePool: [MonType.Support], countMin: 5, countMax: 5, weight: 0.2 },
] },
{ id: "N4", type: TemplateType.NORMAL, tierMin: 6, allowAffix: true,
{ id: "N4", type: TemplateType.NORMAL, tierMin: 3, allowAffix: true,
slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 4, weight: 0.4 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 1, countMax: 3, weight: 0.3 },
{ typePool: [MonType.Support, MonType.Bomber], countMin: 1, countMax: 2, weight: 0.3 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 20, weight: 0.4 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 5, countMax: 15, weight: 0.3 },
{ typePool: [MonType.Support, MonType.Bomber], countMin: 5, countMax: 10, weight: 0.3 },
] },
// ---- MIXED 类 ----
{ id: "M1", type: TemplateType.MIXED, tierMin: 3, allowAffix: true,
{ id: "M1", type: TemplateType.MIXED, tierMin: 2, allowAffix: true,
slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 3, weight: 0.4 },
{ typePool: [MonType.Long], countMin: 1, countMax: 2, weight: 0.3 },
{ typePool: [MonType.Support], countMin: 1, countMax: 1, weight: 0.3 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 15, weight: 0.4 },
{ typePool: [MonType.Long], countMin: 5, countMax: 10, weight: 0.3 },
{ typePool: [MonType.Support], countMin: 5, countMax: 5, weight: 0.3 },
] },
{ id: "M2", type: TemplateType.MIXED, tierMin: 5, allowAffix: true,
{ id: "M2", type: TemplateType.MIXED, tierMin: 3, allowAffix: true,
slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 4, weight: 0.3 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 2, countMax: 3, weight: 0.3 },
{ typePool: [MonType.Bomber], countMin: 1, countMax: 2, weight: 0.2 },
{ typePool: [MonType.Support], countMin: 1, countMax: 1, weight: 0.2 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 20, weight: 0.3 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 10, countMax: 15, weight: 0.3 },
{ typePool: [MonType.Bomber], countMin: 5, countMax: 10, weight: 0.2 },
{ typePool: [MonType.Support], countMin: 5, countMax: 5, weight: 0.2 },
] },
{ id: "M3", type: TemplateType.MIXED, tierMin: 7, allowAffix: true,
{ id: "M3", type: TemplateType.MIXED, tierMin: 4, allowAffix: true,
slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 3, countMax: 4, weight: 0.3 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 2, countMax: 3, weight: 0.25 },
{ typePool: [MonType.Summoner, MonType.Splitter], countMin: 1, countMax: 2, weight: 0.25 },
{ typePool: [MonType.Bomber, MonType.Support], countMin: 1, countMax: 2, weight: 0.2 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 15, countMax: 20, weight: 0.3 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 10, countMax: 15, weight: 0.25 },
{ typePool: [MonType.Summoner, MonType.Splitter], countMin: 5, countMax: 10, weight: 0.25 },
{ typePool: [MonType.Bomber, MonType.Support], countMin: 5, countMax: 10, weight: 0.2 },
] },
// ---- ELITE 类 ----
{ id: "E1", type: TemplateType.ELITE, tierMin: 5, allowAffix: true,
{ id: "E1", type: TemplateType.ELITE, tierMin: 3, allowAffix: true,
slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 1, countMax: 2, weight: 0.5, forceAffix: true },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 1, countMax: 2, weight: 0.5, forceAffix: true },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 5, countMax: 10, weight: 0.5, forceAffix: true },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 5, countMax: 10, weight: 0.5, forceAffix: true },
] },
{ id: "E2", type: TemplateType.ELITE, tierMin: 7, allowAffix: true,
{ id: "E2", type: TemplateType.ELITE, tierMin: 4, allowAffix: true,
slots: [
{ typePool: [MonType.Heavy], countMin: 1, countMax: 2, weight: 0.3, forceAffix: true },
{ typePool: [MonType.Assassin, MonType.Splitter], countMin: 1, countMax: 2, weight: 0.4, forceAffix: true },
{ typePool: [MonType.Bomber], countMin: 1, countMax: 2, weight: 0.3, forceAffix: true },
{ typePool: [MonType.Heavy], countMin: 5, countMax: 10, weight: 0.3, forceAffix: true },
{ typePool: [MonType.Assassin, MonType.Splitter], countMin: 5, countMax: 10, weight: 0.4, forceAffix: true },
{ typePool: [MonType.Bomber], countMin: 5, countMax: 10, weight: 0.3, forceAffix: true },
] },
// ---- BOSS 类 ----
{ id: "B1", type: TemplateType.BOSS, tierMin: 2, allowAffix: true,
{ id: "B1", type: TemplateType.BOSS, tierMin: 1, allowAffix: true,
slots: [
{ typePool: [MonType.MeleeBoss], countMin: 1, countMax: 1, weight: 1.0 },
{ typePool: [MonType.Melee], countMin: 2, countMax: 3, weight: 0.6 },
{ typePool: [MonType.Long], countMin: 0, countMax: 2, weight: 0.4 },
{ typePool: [MonType.Melee], countMin: 10, countMax: 15, weight: 0.6 },
{ typePool: [MonType.Long], countMin: 0, countMax: 10, weight: 0.4 },
] },
{ id: "B2", type: TemplateType.BOSS, tierMin: 4, allowAffix: true,
{ id: "B2", type: TemplateType.BOSS, tierMin: 2, allowAffix: true,
slots: [
{ typePool: [MonType.MeleeBoss], countMin: 1, countMax: 1, weight: 1.0 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 3, weight: 0.5 },
{ typePool: [MonType.Long, MonType.Support], countMin: 1, countMax: 2, weight: 0.5 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 15, weight: 0.5 },
{ typePool: [MonType.Long, MonType.Support], countMin: 5, countMax: 10, weight: 0.5 },
] },
{ id: "B3", type: TemplateType.BOSS, tierMin: 5, allowAffix: true,
{ id: "B3", type: TemplateType.BOSS, tierMin: 3, allowAffix: true,
slots: [
{ typePool: [MonType.MeleeBoss, MonType.LongBoss], countMin: 1, countMax: 1, weight: 1.0 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 4, weight: 0.4 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 1, countMax: 3, weight: 0.3 },
{ typePool: [MonType.Bomber, MonType.Support], countMin: 1, countMax: 2, weight: 0.3 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 20, weight: 0.4 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 5, countMax: 15, weight: 0.3 },
{ typePool: [MonType.Bomber, MonType.Support], countMin: 5, countMax: 10, weight: 0.3 },
] },
{ id: "B4", type: TemplateType.BOSS, tierMin: 7, allowAffix: true,
{ id: "B4", type: TemplateType.BOSS, tierMin: 4, allowAffix: true,
slots: [
{ typePool: [MonType.MeleeBoss, MonType.LongBoss], countMin: 1, countMax: 1, weight: 1.0 },
{ typePool: [MonType.Heavy], countMin: 2, countMax: 3, weight: 0.3 },
{ typePool: [MonType.Assassin, MonType.Splitter], countMin: 1, countMax: 2, weight: 0.3 },
{ typePool: [MonType.Summoner, MonType.Support], countMin: 1, countMax: 2, weight: 0.2 },
{ typePool: [MonType.Bomber], countMin: 1, countMax: 2, weight: 0.2 },
{ typePool: [MonType.Heavy], countMin: 10, countMax: 15, weight: 0.3 },
{ typePool: [MonType.Assassin, MonType.Splitter], countMin: 5, countMax: 10, weight: 0.3 },
{ typePool: [MonType.Summoner, MonType.Support], countMin: 5, countMax: 10, weight: 0.2 },
{ typePool: [MonType.Bomber], countMin: 5, countMax: 10, weight: 0.2 },
] },
// ---- 教程专用 ----
{ id: "TUTORIAL", type: TemplateType.NORMAL, tierMin: 1, allowAffix: false,
slots: [{ typePool: [MonType.Melee], countMin: 1, countMax: 1, weight: 1.0 }] },
slots: [{ typePool: [MonType.Melee], countMin: 5, countMax: 5, weight: 1.0 }] },
]
// ======================== 自适应难度配置 ========================
@@ -598,17 +588,17 @@ export class RogueSpawningEngine {
private consecutiveTemplateCount = 0
/**
* 生成指定波次的怪物列表(主线 1-30 波)
* 生成指定波次的怪物列表(主线 1-15 波)
*
* 内部流程:
* 1. 根据 waveNumber 计算 tier 和 waveInTier
* 2. W1 特殊处理(固定教程模板:1 个 Melee
* 2. W1 特殊处理(固定教程模板:5 个 Melee
* 3. 根据 waveInTier 和 isBossTier 选择蓝图模板
* 4. 计算难度预算 = base_budget × template_modifier × adaptive_factor
* 5. 按模板槽位填充怪物、应用词缀、计算最终属性
*
* @param waveNumber - 波次编号1-30
* 1-3 → Tier 1, 4-6 → Tier 2, ..., 28-30 → Tier 10
* @param waveNumber - 波次编号1-15
* 1-3 → Tier 1, 4-6 → Tier 2, ..., 13-15 → Tier 5
* < 1 返回空数组
*
* @returns GeneratedMonster[] 该波次的怪物实例数组,按 spawnIndex 排列。
@@ -616,9 +606,9 @@ export class RogueSpawningEngine {
* 每次调用结果不同(随机生成),如需固定结果请在同一次调用中缓存。
*
* @example
* // 生成第 1 波(教程:1 个 Melee
* // 生成第 1 波(教程:5 个 Melee
* const w1 = engine.generateWave(1)
* // w1 = [{ uuid: 6001, type: 0, hp: 120, ap: 12, affixes: [], isBoss: false, spawnIndex: 0 }]
* // w1 = [{ uuid: 6001, type: 0, hp: 120, ap: 12, affixes: [], isBoss: false, spawnIndex: 0 }, ...]
*
* // 生成第 6 波Tier 2 Boss 波)
* const w6 = engine.generateWave(6)
@@ -651,11 +641,11 @@ export class RogueSpawningEngine {
/**
* 生成无限模式指定层的指定波
*
* 无限模式结构:每层 4 波REST → NORMAL → MIXED → BOSSTier 从 11 开始递增。
* 无限模式结构:每层 4 波REST → NORMAL → MIXED → BOSSTier 从 6 开始递增。
* 属性倍率按 T(n) = T(n-1) × 1.2 递推,无上限。
*
* @param layer - 层编号(从 1 开始)
* Tier = 10 + layerlayer=1 → Tier 11, multiplier=6.6
* Tier = 5 + layerlayer=1 → Tier 6, multiplier=6.6
* @param waveInLayer - 层内波次编号1-4
* 1: REST恢复波
* 2: NORMAL标准波
@@ -665,12 +655,12 @@ export class RogueSpawningEngine {
* @returns GeneratedMonster[] 该波的怪物实例数组
*
* @example
* // 无限模式第 1 层第 4 波Boss 波Tier 11
* // 无限模式第 1 层第 4 波Boss 波Tier 6
* const boss = engine.generateInfiniteWave(1, 4)
* // boss 包含 1 个 Boss + 若干小怪,属性倍率 = 6.6x
*/
generateInfiniteWave(layer: number, waveInLayer: number): GeneratedMonster[] {
const tier = 10 + layer
const tier = 5 + layer
const tierConfig = getTierConfig(tier)
let templateType: TemplateType
@@ -684,7 +674,7 @@ export class RogueSpawningEngine {
}
const templates = BlueprintTemplates.filter(t =>
t.type === templateType && t.tierMin <= 10
t.type === templateType && t.tierMin <= 5
)
const template = this.pickRandomTemplate(templates)
const budget = Math.round(
@@ -982,7 +972,7 @@ export class RogueSpawningEngine {
* 将 generateWave() 的结果按怪物类型合并,返回简化的 [{type, count}] 格式。
* 注意:此方法不返回 HP/AP 值,调用方需自行处理属性计算或改用 generateWave()。
*
* @param waveNumber - 波次编号1-30
* @param waveNumber - 波次编号1-15
* @returns IWaveSlot[] 按怪物类型合并后的数组
* - type: MonType 枚举值
* - count: 该类型的怪物总数
@@ -990,8 +980,8 @@ export class RogueSpawningEngine {
*
* @example
* engine.getWaveSlotConfig(6)
* // [{ type: 8, count: 1 }, { type: 0, count: 2 }, { type: 2, count: 1 }]
* // = 1 个 MeleeBoss + 2 个 Melee + 1 个 Long
* // [{ type: 8, count: 1 }, { type: 0, count: 10 }, { type: 2, count: 5 }]
* // = 1 个 MeleeBoss + 10 个 Melee + 5 个 Long
*/
getWaveSlotConfig(waveNumber: number): IWaveSlot[] {
const generated = this.generateWave(waveNumber)
@@ -1035,14 +1025,14 @@ export function getWaveSlotConfig(waveNumber: number): IWaveSlot[] {
}
/**
* 向后兼容:默认占位配置(波次 > 30 或异常时的兜底配置)
* 4 个 Melee + 3 个 Long + 1 个 Support + 1 个 Bomber
* 向后兼容:默认占位配置(波次 > 15 或异常时的兜底配置)
* 20 个 Melee + 15 个 Long + 5 个 Support + 5 个 Bomber
*/
export const DefaultWaveSlot: IWaveSlot[] = [
{ type: MonType.Melee, count: 4 },
{ type: MonType.Long, count: 3 },
{ type: MonType.Support, count: 1 },
{ type: MonType.Bomber, count: 1 },
{ type: MonType.Melee, count: 20 },
{ type: MonType.Long, count: 15 },
{ type: MonType.Support, count: 5 },
{ type: MonType.Bomber, count: 5 },
]
/**
@@ -1053,14 +1043,14 @@ export const DefaultWaveSlot: IWaveSlot[] = [
*
* @example
* WaveSlotConfig[5] // 动态生成第 5 波的 IWaveSlot[]
* WaveSlotConfig[30] // 动态生成第 30 波的 IWaveSlot[]
* WaveSlotConfig[15] // 动态生成第 15 波的 IWaveSlot[]
*/
export const WaveSlotConfig: { [wave: number]: IWaveSlot[] } = new Proxy(
{} as { [wave: number]: IWaveSlot[] },
{
get(_target, prop: string) {
const wave = parseInt(prop, 10)
if (!isNaN(wave) && wave >= 1 && wave <= 30) {
if (!isNaN(wave) && wave >= 1 && wave <= 15) {
return spawningEngine.getWaveSlotConfig(wave)
}
if (prop === "toJSON") return () => ({})
@@ -1068,7 +1058,7 @@ export const WaveSlotConfig: { [wave: number]: IWaveSlot[] } = new Proxy(
},
has(_target, prop: string) {
const wave = parseInt(prop, 10)
return !isNaN(wave) && wave >= 1 && wave <= 30
return !isNaN(wave) && wave >= 1 && wave <= 15
},
}
)