refactor(map): 重构rogue战斗数值生成逻辑
重构了Rogue模式的怪物数值计算方式,移除了基于英雄实时强度的动态缩放算法,改为统一使用基础属性乘以可调系数的固定缩放方案: 1. 更新了怪物配置基础属性,对齐1级英雄面板作为基准 2. 简化了数值公式,仅保留三个可调节的全局系数用于平衡难度 3. 删除了冗余的ECS查询、强度计算和类型系数逻辑 4. 更新了全部注释文档,明确数值校准目标和调整方式
This commit is contained in:
@@ -11,23 +11,19 @@
|
||||
* 设计层次:
|
||||
* 1. BossComboLibrary - Boss 组合库(小怪构成 + Boss/小怪技能池 + 抽取权重)
|
||||
* 2. MonSkillPool/BossSkillPool - 怪物技能池(atking / atked / dead 等全触发类型)
|
||||
* 3. RogueSpawningEngine - 生成引擎(按英雄强度反推怪物强度)
|
||||
* 3. RogueSpawningEngine - 生成引擎(基础属性 × 可调系数)
|
||||
*
|
||||
* 核心公式:
|
||||
* heroPower = Σ calcHeroPower(HeroInfo[uuid], lv) (场上存活英雄)
|
||||
* targetPower = heroPower × BOSS_TYPE_RATIO × FINAL_HP_MUL × MON_POWER_MUL
|
||||
* hpScale = targetPower ÷ Σ 怪物基础强度
|
||||
* 怪物 hp = base_hp × hpScale (走总强度框架,自动匹配玩家)
|
||||
*
|
||||
* 怪物 ap 独立挂钩玩家输出(不走总强度,避免被 hp 挤占):
|
||||
* 怪物 ap = 玩家平均攻击 × AP_GROWTH_FINAL × AP_RELATIVE × 类型系数
|
||||
* 核心公式(基础属性 × 可调系数):
|
||||
* 怪物 hp = HeroInfo 基础hp × MON_HP_MUL(Boss 额外 × BOSS_HP_BONUS_MUL)
|
||||
* 怪物 ap = HeroInfo 基础ap × MON_AP_MUL
|
||||
* 怪物基础面板与 1 级英雄持平(近战镜像 hp300/ap22/cd1.8),默认系数校准为
|
||||
* 整场战斗(3 英雄 vs 4 小怪 + 1 Boss)双方对砍 ≈ WAVE_DURATION(30s)。
|
||||
* 手感微调只改 MON_HP_MUL / MON_AP_MUL / BOSS_HP_BONUS_MUL 三个系数即可。
|
||||
*/
|
||||
|
||||
import { HeroInfo, MonType, MonTypeName, calcHeroPower, TriggerGrouped, LvReviveEntry } from "../common/config/heroSet";
|
||||
import { HeroInfo, MonType, MonTypeName, TriggerGrouped, LvReviveEntry } from "../common/config/heroSet";
|
||||
import { SkillOverrides } from "../common/config/SkillSet";
|
||||
import { FacSet } from "../common/config/GameSet";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
|
||||
// ======================== 1. 战斗常量 ========================
|
||||
|
||||
@@ -38,17 +34,13 @@ export const WAVE_DURATION = 30;
|
||||
export const BOSS_COUNT = 1;
|
||||
export const MINION_COUNT = 4;
|
||||
|
||||
/** Boss 战强度系数(原压力回合系数) */
|
||||
export const BOSS_TYPE_RATIO = 1.2;
|
||||
/** 最终 Boss 战 HP 乘区(原 wave 20 配置) */
|
||||
export const FINAL_HP_MUL = 1.8;
|
||||
/** 全局怪物强度系数(1 = 怪物强度与英雄能力持平,同时作用于 hp 缩放与 ap 挂钩公式) */
|
||||
export const MON_POWER_MUL = 1;
|
||||
/** 怪物 ap 最终递进系数(原 wave 20 满档 3.0) */
|
||||
export const AP_GROWTH_FINAL = 3.0;
|
||||
/** 怪物 ap 相对玩家平均攻击的基础系数(<1 保证单怪不至于一击致命,由 Boss/精英类型再上浮) */
|
||||
export const AP_RELATIVE = 0.6;
|
||||
/** Boss 血量加成(原 wave 20 bossBonusHpMul = 1.6) */
|
||||
/** 怪物血量可调系数:最终 hp = HeroInfo 基础hp × MON_HP_MUL。
|
||||
* 2.6 = 校准后怪物总血量 / 3 英雄 30s 输出(≈30 DPS),双方对砍 ≈ 30s 分胜负 */
|
||||
export const MON_HP_MUL = 2.6;
|
||||
/** 怪物攻击可调系数:最终 ap = HeroInfo 基础ap × MON_AP_MUL。
|
||||
* 0.9 使 4 小怪+1 Boss 的总 DPS ≈ 20,30s 恰好磨光 3 英雄总血量(≈600) */
|
||||
export const MON_AP_MUL = 0.9;
|
||||
/** Boss 血量额外加成(在 MON_HP_MUL 之上乘算,突出压场定位) */
|
||||
export const BOSS_HP_BONUS_MUL = 1.6;
|
||||
/** 战斗超时(秒):超过后强制结算整局(残留怪销毁并扣留存分) */
|
||||
export const BATTLE_TIMEOUT = 90;
|
||||
@@ -371,12 +363,12 @@ export const TestModeConfig = {
|
||||
|
||||
/**
|
||||
* Boss 组合生成引擎
|
||||
* 随机抽一组 Boss 组合,按英雄强度反推怪物强度(最终 Boss 战档位)
|
||||
* 随机抽一组 Boss 组合,怪物最终属性 = HeroInfo 基础属性 × 可调系数
|
||||
*/
|
||||
export class RogueSpawningEngine {
|
||||
|
||||
/**
|
||||
* 生成一场战斗的怪物列表:随机一组 Boss 组合(1 Boss + 组合小怪)。
|
||||
* 生成一场战斗的怪物列表:随机一组 Boss 组合(1 Boss + 4 小怪,属性走基础×系数)。
|
||||
* Boss 压队尾(登场时由 MissionMonComp 统一布位)。
|
||||
* @returns 怪物列表(小怪在前、Boss 在尾)
|
||||
*/
|
||||
@@ -406,21 +398,10 @@ export class RogueSpawningEngine {
|
||||
monsters.push(this.makeBoss());
|
||||
}
|
||||
|
||||
// 属性缩放:直接取最终 Boss 战档位系数
|
||||
const heroPower = this.getCurrentHeroPower();
|
||||
const targetPower = heroPower * BOSS_TYPE_RATIO * MON_POWER_MUL;
|
||||
const totalBasePower = monsters.reduce((sum, m) => {
|
||||
const info = HeroInfo[m.uuid];
|
||||
return sum + (info ? calcHeroPower(info, 1) : m.hp + m.ap);
|
||||
}, 0);
|
||||
if (totalBasePower > 0 && targetPower > 0) {
|
||||
const hpScale = (targetPower * FINAL_HP_MUL) / totalBasePower;
|
||||
const heroAvgAp = this.getCurrentHeroAttack();
|
||||
for (const m of monsters) {
|
||||
m.hp = Math.max(1, Math.round(m.hp * hpScale));
|
||||
// ap = 玩家平均攻击 × 最终递进 × 基础系数 × 全局强度系数 × 类型系数
|
||||
m.ap = Math.max(1, Math.round(heroAvgAp * AP_GROWTH_FINAL * AP_RELATIVE * MON_POWER_MUL * this.getApTypeFactor(m)));
|
||||
}
|
||||
// 属性缩放:基础属性 × 可调系数(Boss 血量额外加成),校准目标双方对砍 ≈ 30s
|
||||
for (const m of monsters) {
|
||||
m.hp = Math.max(1, Math.round(m.hp * MON_HP_MUL * (m.isBoss ? BOSS_HP_BONUS_MUL : 1)));
|
||||
m.ap = Math.max(1, Math.round(m.ap * MON_AP_MUL));
|
||||
}
|
||||
|
||||
// 挂载组合技能(Boss 专属池 / 小怪普通池)
|
||||
@@ -443,45 +424,6 @@ export class RogueSpawningEngine {
|
||||
reset(): void {
|
||||
}
|
||||
|
||||
// ======================== 私有生成子算法 ========================
|
||||
|
||||
/**
|
||||
* 计算场上存活英雄的总强度
|
||||
* 通过 ECS 查询所有 HeroAttrsComp,累加 calcHeroPower
|
||||
*/
|
||||
private getCurrentHeroPower(): number {
|
||||
let total = 0;
|
||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
if (!attrs || attrs.is_dead || attrs.fac !== FacSet.HERO) return;
|
||||
const info = HeroInfo[attrs.hero_uuid];
|
||||
if (!info) return;
|
||||
// 使用英雄当前等级(升级卡驱动),保证强度评估与实战一致
|
||||
const lv = Math.max(1, attrs.lv || 1);
|
||||
total += calcHeroPower(info, lv);
|
||||
});
|
||||
// 兜底:无英雄时返回基准强度,避免除零
|
||||
return Math.max(total, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算场上存活英雄的输出强度(用于怪物 ap 挂钩玩家,独立于总强度框架)。
|
||||
* 取玩家英雄的"实时 ap 平均",怪物 ap 以此为锚,保证玩家输出越高、被反击越痛。
|
||||
*/
|
||||
private getCurrentHeroAttack(): number {
|
||||
let totalAp = 0;
|
||||
let count = 0;
|
||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
if (!attrs || attrs.is_dead || attrs.fac !== FacSet.HERO) return;
|
||||
totalAp += Math.max(1, attrs.ap || 1);
|
||||
count++;
|
||||
});
|
||||
// 兜底:无英雄时返回基准攻击,避免除零
|
||||
if (count === 0) return 30;
|
||||
return totalAp / count; // 平均单英雄攻击
|
||||
}
|
||||
|
||||
/** 测试模式:完全绕过引擎逻辑 */
|
||||
private generateTestWave(): GeneratedMonster[] {
|
||||
const count = Math.max(1, TestModeConfig.spawnCount || 1);
|
||||
@@ -536,20 +478,6 @@ export class RogueSpawningEngine {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 怪物 ap 类型系数:Boss 上浮,远程/召唤稍降(避免玻璃大炮互秒),近战/重甲基准。
|
||||
*/
|
||||
private getApTypeFactor(m: GeneratedMonster): number {
|
||||
if (m.isBoss) return 2.2; // Boss 单发重,符合"压场"定位
|
||||
if (m.isBossGuard) return 1.2; // 护卫略高于杂鱼
|
||||
switch (m.type) {
|
||||
case MonType.Long: return 0.9;
|
||||
case MonType.Summoner: return 0.7;
|
||||
case MonType.Assassin: return 1.1;
|
||||
default: return 1.0; // Melee / Heavy
|
||||
}
|
||||
}
|
||||
|
||||
/** 生成普通怪物 */
|
||||
private makeMonster(type: MonType, spawnIndex: number): GeneratedMonster {
|
||||
let uuids = MonList[type];
|
||||
|
||||
Reference in New Issue
Block a user