docs: 更新肉鸽刷怪系统文档与配置至15波5阶梯版本
同步调整了文档中的波次、阶梯、词缀、预算等全部数值配置,同时更新了RogueConfig.ts中的代码配置,将原30波10阶梯架构重构为15波5阶梯版本,包括调整怪物生成模板、属性倍率、词缀解锁等级和无限模式起始阶梯等核心参数。
This commit is contained in:
@@ -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-10,value = 该阶梯的完整配置
|
||||
* 主线 30 波映射:wave 1-3 → T1, wave 4-6 → T2, ..., wave 28-30 → T10
|
||||
* 5 阶梯配置表
|
||||
* key = 阶梯编号 1-5,value = 该阶梯的完整配置
|
||||
* 主线 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 → BOSS),Tier 从 11 开始递增。
|
||||
* 无限模式结构:每层 4 波(REST → NORMAL → MIXED → BOSS),Tier 从 6 开始递增。
|
||||
* 属性倍率按 T(n) = T(n-1) × 1.2 递推,无上限。
|
||||
*
|
||||
* @param layer - 层编号(从 1 开始)
|
||||
* Tier = 10 + layer(layer=1 → Tier 11, multiplier=6.6)
|
||||
* Tier = 5 + layer(layer=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
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user