feat(怪物生成): 添加全局配置系统并优化怪物生成逻辑

引入全局配置接口 IRogueGlobalConfig 用于集中管理怪物生成参数
添加配置获取和更新方法 getRogueConfig/updateRogueConfig
修改生成逻辑使用配置参数控制间隔、数量限制和预算计算
增加单次生成数量限制和同屏怪物数量限制
This commit is contained in:
walkpan
2026-01-03 09:28:04 +08:00
parent 2591fb849e
commit 7583ca7a37
2 changed files with 102 additions and 43 deletions

View File

@@ -6,7 +6,7 @@ import { MonStart } from "../common/config/heroSet";
import { smc } from "../common/SingletonModuleComp";
import { GameEvent } from "../common/config/GameEvent";
// 导入肉鸽配置
import { getStageMonConfigs, MonType, generateMonstersFromBudget } from "./RogueConfig";
import { getStageMonConfigs, MonType, generateMonstersFromBudget, getRogueConfig } from "./RogueConfig";
import { BuffConf } from "../common/config/SkillSet";
import { IndexSet, FacSet } from "../common/config/GameSet";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
@@ -64,30 +64,34 @@ export class MissionMonCompComp extends CCComp {
// 累加游戏时间
this.gameTime += dt;
const config = getRogueConfig();
// ==========================================
// 新增:每秒执行一次刷怪逻辑 (Threat Budget)
// ==========================================
this.spawnLogicTimer += dt;
if (this.spawnLogicTimer >= 1.0) {
if (this.spawnLogicTimer >= config.spawnLogicInterval) {
this.spawnLogicTimer = 0;
// 获取英雄血量比例
const hpRatio = this.getHeroHpRatio();
// 生成怪物
const newMonsters = generateMonstersFromBudget(this.gameTime, hpRatio);
// 添加到队列
newMonsters.forEach(mon => {
this.addToStageSpawnQueue(
mon.uuid,
mon.position !== undefined ? mon.position : 0,
mon.type,
mon.level,
mon.buffs || []
);
});
// 检查最大怪物数量限制
if (smc.vmdata.mission_data.mon_num < config.maxMonsterCount) {
// 获取英雄血量比例
const hpRatio = this.getHeroHpRatio();
// 生成怪物
const newMonsters = generateMonstersFromBudget(this.gameTime, hpRatio);
// 添加到队列
newMonsters.forEach(mon => {
this.addToStageSpawnQueue(
mon.uuid,
mon.position !== undefined ? mon.position : 0,
mon.type,
mon.level,
mon.buffs || []
);
});
}
}
// 处理随机事件
@@ -98,7 +102,7 @@ export class MissionMonCompComp extends CCComp {
this.spawnTimer += dt;
// 正常召唤间隔
if (this.spawnTimer >= this.spawnInterval) {
if (this.spawnTimer >= config.spawnInterval) {
this.spawnNextMonster();
this.spawnTimer = 0;
}

View File

@@ -21,7 +21,7 @@
import { HeroInfo } from "../common/config/heroSet";
// 精英怪物配置表
@@ -241,28 +241,87 @@ function getSpawnWeights(timeInSeconds: number): SpawnWeight[] {
}
}
/**
* 全局刷怪配置接口
*/
export interface IRogueGlobalConfig {
/** 场上最大怪物数量限制 */
maxMonsterCount: number;
/** 刷怪逻辑执行间隔(秒) - 决定多久计算一次生成 */
spawnLogicInterval: number;
/** 单个怪物生成间隔(秒) - 队列中怪物的实际生成频率 */
spawnInterval: number;
/** 基础威胁预算 - 每秒产生的基础点数 */
baseBudget: number;
/** 时间难度因子 - 每分钟增加的预算比例 (0.2 = 20%) */
timeDifficultyFactor: number;
/** 绝地求生阈值 - 英雄血量低于此比例时触发减缓刷怪 */
survivalHpThreshold: number;
/** 绝地求生预算乘数 - 触发阈值时的预算折扣 */
survivalBudgetMultiplier: number;
/** 单次逻辑最大生成怪物数限制 - 防止瞬间生成过多 */
maxSpawnPerLogic: number;
}
/**
* 默认配置
*/
export const DefaultRogueConfig: IRogueGlobalConfig = {
maxMonsterCount: 50, // 默认同屏50只
spawnLogicInterval: 1.0, // 每秒计算一次
spawnInterval: 0.6, // 队列出怪间隔0.6秒
baseBudget: 1.5, // 基础预算
timeDifficultyFactor: 0.2, // 每分钟增加20%预算
survivalHpThreshold: 0.4, // 40%血量触发保护
survivalBudgetMultiplier: 0.7, // 保护时预算打7折
maxSpawnPerLogic: 10 // 单次最多生成10只
};
// 当前配置实例
let currentConfig: IRogueGlobalConfig = { ...DefaultRogueConfig };
/**
* 获取当前全局配置
*/
export function getRogueConfig(): IRogueGlobalConfig {
return currentConfig;
}
/**
* 更新全局配置
* @param config 部分或全部配置
*/
export function updateRogueConfig(config: Partial<IRogueGlobalConfig>) {
currentConfig = {
...currentConfig,
...config
};
console.log("[RogueConfig] Configuration updated:", currentConfig);
}
/**
* 计算当前威胁点数预算
* @param timeInSeconds 游戏时间(秒)
* @param heroHpRatio 英雄血量比例 (0-1)
*/
export function calculateBudget(timeInSeconds: number, heroHpRatio: number = 1.0): number {
// 基础预算:每秒产生的点数
// 假设1分钟时为20点公式: Budget = Base * (1 + 60/60 * 0.2) = Base * 1.2 = 20 => Base ≈ 16
// 修改:降低基础预算,避免队列积压。每秒 1.5 点左右(减半)。
const Base_Budget = 1.5;
const config = getRogueConfig();
// 基础预算
const Base_Budget = config.baseBudget;
// 时间因子:随时间增加难度
const timeFactor = 1 + (timeInSeconds / 60) * 0.2;
const timeFactor = 1 + (timeInSeconds / 60) * config.timeDifficultyFactor;
// 强度乘数 (DDA):根据英雄状态调整
let intensityMultiplier = 1.0;
if (heroHpRatio < 0.4) {
// 绝地求生:血量低于40%时,预算缩减30%
intensityMultiplier = 0.7;
if (heroHpRatio < config.survivalHpThreshold) {
// 绝地求生:血量低于阈值时预算缩减
intensityMultiplier = config.survivalBudgetMultiplier;
}
// 公式: Budget(t) = Base_Budget * (1 + t/60 * 0.2) * Intensity_Multiplier
// 公式: Budget(t) = Base_Budget * (1 + t/60 * Factor) * Intensity_Multiplier
return Math.floor(Base_Budget * timeFactor * intensityMultiplier);
}
@@ -272,17 +331,15 @@ export function calculateBudget(timeInSeconds: number, heroHpRatio: number = 1.0
* @param heroHpRatio 英雄血量比例
*/
export function generateMonstersFromBudget(timeInSeconds: number, heroHpRatio: number = 1.0): IMonsConfig[] {
// 15分钟后只生成Boss且如果已经有Boss可能需要控制这里简化为每次调用都尝试生成由外部控制频率或唯一性
// 但根据"清除所有杂兵锁定刷出最终BOSS"这里只返回Boss配置
const config = getRogueConfig();
// 15分钟后只生成Boss
if (timeInSeconds >= 15 * 60) {
// 可以在这里做特殊处理比如只返回一个Boss或者返回空如果Boss已存在
// 假设外部每秒调用这里返回Boss外部队列会堆积。
// 建议Boss波次由特殊逻辑控制或者这里返回Boss但消耗巨大预算
return [{
uuid: 5701,
type: MonType.BOSS,
level: Math.floor(timeInSeconds / 60), // 等级随时间
position: 2 // 中间位置
level: Math.floor(timeInSeconds / 60),
position: 2
}];
}
@@ -300,29 +357,27 @@ export function generateMonstersFromBudget(timeInSeconds: number, heroHpRatio: n
if (pool.length === 0) return [];
let attempts = 0;
// 尝试消耗预算直到耗尽或无法购买最便宜的怪物
const minCost = Math.min(...weights.map(w => MonsterCost[w.uuid] || 1));
while (currentBudget >= minCost && attempts < 50) {
// 限制单次生成最大数量
while (currentBudget >= minCost && attempts < 50 && monsters.length < config.maxSpawnPerLogic) {
attempts++;
// 随机选择怪物
const uuid = pool[Math.floor(Math.random() * pool.length)];
const cost = MonsterCost[uuid] || 1;
if (currentBudget >= cost) {
currentBudget -= cost;
// 确定怪物类型
let type = MonType.NORMAL;
if (uuid === 5701) type = MonType.BOSS; // 或者是精英,视情况而定
if (uuid === 5701) type = MonType.BOSS;
else if (MonsterCost[uuid] >= 10) type = MonType.ELITE;
monsters.push({
uuid: uuid,
type: type,
level: Math.floor(timeInSeconds / 60) + 1, // 简单等级计算实际属性由getMonAttr处理
position: Math.floor(Math.random() * 5) // 随机位置 0-4
level: Math.floor(timeInSeconds / 60) + 1,
position: Math.floor(Math.random() * 5)
});
}
}