feat(肉鸽): 实现动态难度和配置化刷怪系统
- 新增 RogueConfig 配置文件,定义怪物类型、成长曲线和刷新参数 - 重构 MissionMonComp 刷怪逻辑,支持普通怪、Boss 和特殊队列三种生成方式 - 引入阶段成长机制,怪物属性随游戏时间动态增强 - 添加随机成长类型,使同阶段怪物属性具有差异性 - 支持外部事件插队刷怪,用于运营活动和技能召唤等场景
This commit is contained in:
@@ -3,12 +3,12 @@ import { mLogger } from "../common/Logger";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { Monster } from "../hero/Mon";
|
||||
import { MonStart } from "../common/config/heroSet";
|
||||
import { HeroInfo, MonStart } from "../common/config/heroSet";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
// 导入新的肉鸽配置
|
||||
import { BuffConf } from "../common/config/SkillSet";
|
||||
import {BoxSet } from "../common/config/GameSet";
|
||||
import { BossList, BossSpawnCd, MonList, MonType, SpawnBaseCd, SpawnMinCd, SpawnPowerBias, SpawnStageReduce, StageBossGrow, StageDuration, StageGrow, UpType } from "./RogueConfig";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** 视图层对象 */
|
||||
@@ -18,42 +18,44 @@ export class MissionMonCompComp extends CCComp {
|
||||
@property({ tooltip: "是否启用调试日志" })
|
||||
private debugMode: boolean = false;
|
||||
|
||||
// 刷怪队列 (主要用于特殊事件插队)
|
||||
// 刷怪队列(用于插队生成:比如运营活动怪、技能召唤怪、剧情强制怪)
|
||||
// 约定:队列里的怪会优先于常规刷新处理
|
||||
private MonQueue: Array<{
|
||||
uuid: number,
|
||||
level: number,
|
||||
}> = [];
|
||||
|
||||
private spawnCount: number = 0; // 召唤计数器
|
||||
/** 全局生成顺序计数器,用于层级管理 */
|
||||
private spawnCount: number = 0; // 总生成计数,用于控制横向分布位置
|
||||
/** 全局生成顺序计数器,用于层级管理(预留) */
|
||||
private globalSpawnOrder: number = 0;
|
||||
/** 游戏进行时间(秒) */
|
||||
/** 局内战斗运行时间(秒) */
|
||||
private gameTime: number = 0;
|
||||
/** 波次刷怪计时器 */
|
||||
/** 普通怪刷新计时器 */
|
||||
private waveTimer: number = 0;
|
||||
/** 队列处理计时器 */
|
||||
/** 插队刷怪处理计时器 */
|
||||
private queueTimer: number = 0;
|
||||
/** Boss 刷新计时器 */
|
||||
private bossTimer: number = 0;
|
||||
onLoad(){
|
||||
// 关卡准备、切换波次时重置刷怪状态
|
||||
this.on(GameEvent.FightReady,this.fight_ready,this)
|
||||
this.on(GameEvent.NewWave,this.fight_ready,this)
|
||||
// 监听特殊刷怪事件 (精英/Boss)
|
||||
// 支持外部模块主动推入特殊怪(优先级高于常规刷新)
|
||||
this.on("SpawnSpecialMonster", this.onSpawnSpecialMonster, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理特殊刷怪事件
|
||||
* @param event 事件名
|
||||
* @param args 参数 { uuid, type, level, position?, buffs? }
|
||||
* 接收特殊刷怪事件并入队
|
||||
* 事件数据最小结构:{ uuid, level }
|
||||
*/
|
||||
private onSpawnSpecialMonster(event: string, args: any) {
|
||||
if (!args) return;
|
||||
mLogger.log(this.debugMode, 'MissionMonComp', `[MissionMonComp] 收到特殊刷怪指令:`, args);
|
||||
// 插入队列
|
||||
this.MonQueue.push({
|
||||
uuid: args.uuid,
|
||||
level: args.level,
|
||||
});
|
||||
// 立即触发一次队列检查 (可选,让 update 尽快处理)
|
||||
// 让队列在下一帧附近尽快消费,提升事件响应感
|
||||
this.queueTimer = 1.0;
|
||||
}
|
||||
|
||||
@@ -61,12 +63,13 @@ export class MissionMonCompComp extends CCComp {
|
||||
}
|
||||
|
||||
fight_ready(){
|
||||
// 仅重置“本组件”刷怪状态,不处理其他系统状态
|
||||
smc.vmdata.mission_data.mon_num=0
|
||||
// 重置生成顺序计数器
|
||||
this.globalSpawnOrder = 0
|
||||
this.gameTime = 0
|
||||
this.waveTimer = 0
|
||||
this.queueTimer = 0
|
||||
this.bossTimer = 0
|
||||
this.MonQueue = []
|
||||
this.spawnCount = 0
|
||||
|
||||
@@ -74,36 +77,142 @@ export class MissionMonCompComp extends CCComp {
|
||||
}
|
||||
|
||||
protected update(dt: number): void {
|
||||
// 统一战斗状态门禁:未开战/暂停/角色死亡阶段均不刷怪
|
||||
if(!smc.mission.play) return
|
||||
if(smc.mission.pause) return
|
||||
// 如果英雄死亡(停止怪物行动标志为true),则停止刷怪逻辑
|
||||
if(smc.mission.stop_mon_action) return;
|
||||
if(!smc.mission.in_fight) return;
|
||||
|
||||
// 累加游戏时间
|
||||
// 计时推进:所有“按时间驱动”的曲线都依赖 gameTime
|
||||
this.gameTime += dt;
|
||||
|
||||
|
||||
// 刷怪优先级:特殊队列 > 普通怪 > Boss(都满足条件时同帧可连续执行)
|
||||
this.updateSpecialQueue(dt);
|
||||
this.updateNormalSpawn(dt);
|
||||
this.updateBossSpawn(dt);
|
||||
|
||||
}
|
||||
|
||||
private updateSpecialQueue(dt: number) {
|
||||
if (this.MonQueue.length <= 0) return;
|
||||
this.queueTimer += dt;
|
||||
// 轻微节流,避免同帧内突发大量插队导致瞬间堆怪
|
||||
if (this.queueTimer < 0.15) return;
|
||||
this.queueTimer = 0;
|
||||
const item = this.MonQueue.shift();
|
||||
if (!item) return;
|
||||
// 特殊怪同样走随机成长类型,保持局内随机性一致
|
||||
const upType = this.getRandomUpType();
|
||||
this.addMonster(item.uuid, this.spawnCount, BossList.includes(item.uuid), upType);
|
||||
this.spawnCount += 1;
|
||||
}
|
||||
|
||||
private updateNormalSpawn(dt: number) {
|
||||
this.waveTimer += dt;
|
||||
// 普通怪刷新间隔会随 stage 增长而缩短
|
||||
const spawnCd = this.getCurrentSpawnCd();
|
||||
if (this.waveTimer < spawnCd) return;
|
||||
this.waveTimer = 0;
|
||||
// 先随机怪物类型,再在该类型池内随机具体 uuid
|
||||
const uuid = this.getRandomNormalMonsterUuid();
|
||||
// 每只怪独立抽取成长方案,使同时间段怪群有轻微离散性
|
||||
const upType = this.getRandomUpType();
|
||||
this.addMonster(uuid, this.spawnCount, false, upType);
|
||||
this.spawnCount += 1;
|
||||
}
|
||||
|
||||
private updateBossSpawn(dt: number) {
|
||||
this.bossTimer += dt;
|
||||
// Boss 按固定周期出现,不受普通怪 CD 影响
|
||||
if (this.bossTimer < BossSpawnCd) return;
|
||||
this.bossTimer = 0;
|
||||
const uuid = this.getRandomBossUuid();
|
||||
const upType = this.getRandomUpType();
|
||||
this.addMonster(uuid, this.spawnCount, true, upType);
|
||||
this.spawnCount += 1;
|
||||
}
|
||||
|
||||
private getCurrentStage(): number {
|
||||
// 每 30 秒提升 1 阶(由配置 StageDuration 控制)
|
||||
return Math.floor(this.gameTime / StageDuration);
|
||||
}
|
||||
|
||||
private getCurrentSpawnCd(): number {
|
||||
const stage = this.getCurrentStage();
|
||||
const cd = SpawnBaseCd - stage * SpawnStageReduce;
|
||||
// 保护下限,避免后期刷新间隔过小打爆性能
|
||||
return Math.max(SpawnMinCd, cd);
|
||||
}
|
||||
|
||||
private getRandomUpType(): UpType {
|
||||
// 从 StageGrow 的 key 中采样,保证新增配置无需改逻辑
|
||||
const keys = Object.keys(StageGrow).map(v => Number(v) as UpType);
|
||||
const index = Math.floor(Math.random() * keys.length);
|
||||
return keys[index] ?? UpType.AP1_HP1;
|
||||
}
|
||||
|
||||
private getRandomNormalMonsterUuid(): number {
|
||||
// MonType 是常量对象,这里通过值采样拿到怪物类型 id
|
||||
const typeKeys = Object.keys(MonType).map(k => (MonType as any)[k]).filter(v => typeof v === "number");
|
||||
const randomType = typeKeys[Math.floor(Math.random() * typeKeys.length)] as number;
|
||||
// 如果某类型配置被清空,回退到 AP 类型,避免空池异常
|
||||
const pool = MonList[randomType] || MonList[MonType.AP];
|
||||
const index = Math.floor(Math.random() * pool.length);
|
||||
return pool[index];
|
||||
}
|
||||
|
||||
private getRandomBossUuid(): number {
|
||||
// 目前 Boss 池可扩展为多个,先走随机抽取
|
||||
const index = Math.floor(Math.random() * BossList.length);
|
||||
return BossList[index];
|
||||
}
|
||||
|
||||
private resolveGrowPair(upType: UpType, isBoss: boolean): [number, number] {
|
||||
// 普通怪基础成长:StageGrow
|
||||
const grow = StageGrow[upType] || StageGrow[UpType.AP1_HP1];
|
||||
if (!isBoss) return [grow[0], grow[1]];
|
||||
// Boss 额外成长:StageBossGrow(在普通成长上叠加)
|
||||
const bossGrow = StageBossGrow[upType] || StageBossGrow[UpType.AP1_HP1];
|
||||
return [grow[0] + bossGrow[0], grow[1] + bossGrow[1]];
|
||||
}
|
||||
|
||||
private getSpawnPowerBias(): number {
|
||||
// 动态难度偏差入口:当前固定读取配置,后续可切到玩家表现驱动
|
||||
return SpawnPowerBias;
|
||||
}
|
||||
|
||||
|
||||
private addMonster(
|
||||
uuid: number = 1001,
|
||||
i: number = 0,
|
||||
isBoss: boolean = false,
|
||||
upType: UpType = UpType.AP1_HP1,
|
||||
) {
|
||||
// 创建 ECS 怪物实体
|
||||
let mon = ecs.getEntity<Monster>(Monster);
|
||||
let scale = -1;
|
||||
// 按生成序号做横向错列,减轻重叠感
|
||||
const x = MonStart.START_X + Math.floor(i / 4) * MonStart.START_I;
|
||||
let y = BoxSet.GAME_LINE;
|
||||
|
||||
let pos: Vec3 = v3(x, y, 0);
|
||||
|
||||
// 递增全局生成顺序 - 溢出保护
|
||||
// 递增全局生成顺序,做溢出保护
|
||||
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999;
|
||||
|
||||
// 生成怪物
|
||||
mon.load(pos, scale, uuid, false);
|
||||
// 先用原始配置创建怪物,再覆盖成长后属性
|
||||
mon.load(pos, scale, uuid, isBoss);
|
||||
const model = mon.get(HeroAttrsComp);
|
||||
const base = HeroInfo[uuid];
|
||||
if (!model || !base) return;
|
||||
const stage = this.getCurrentStage();
|
||||
const grow = this.resolveGrowPair(upType, isBoss);
|
||||
// 偏差值用于整体系数缩放:1=不变,>1增强,<1减弱
|
||||
const bias = Math.max(0.1, this.getSpawnPowerBias());
|
||||
// 最终公式:基础值 + 阶段成长,再乘偏差
|
||||
model.ap = Math.max(1, Math.floor((base.ap + stage * grow[0]) * bias));
|
||||
model.hp_max = Math.max(1, Math.floor((base.hp + stage * grow[1]) * bias));
|
||||
// 满血登场,保证 hp/hp_max 一致
|
||||
model.hp = model.hp_max;
|
||||
}
|
||||
|
||||
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
|
||||
|
||||
@@ -1 +1,38 @@
|
||||
|
||||
export enum UpType {
|
||||
AP1_HP1 = 0, //平衡
|
||||
HP2 = 1, //强hp
|
||||
AP2 = 2 //强ap
|
||||
}
|
||||
export const StageGrow = {
|
||||
[UpType.AP1_HP1]: [4,10],
|
||||
[UpType.HP2]: [2,20],
|
||||
[UpType.AP2]: [8,0],
|
||||
}
|
||||
export const StageBossGrow = {
|
||||
[UpType.AP1_HP1]: [3,16],
|
||||
[UpType.HP2]: [1,24],
|
||||
[UpType.AP2]: [10,4],
|
||||
}
|
||||
export const MonType = {
|
||||
AP: 0, // 近战高功
|
||||
SPEED: 1, // 高速贴近
|
||||
HP: 2, // 高血皮厚
|
||||
//远程攻击
|
||||
//
|
||||
}
|
||||
export const MonList = {
|
||||
[MonType.AP]: [5201,5401], // 近战高功
|
||||
[MonType.SPEED]: [5301], // 高速贴近
|
||||
[MonType.HP]: [5501], // 高血皮厚
|
||||
//远程攻击
|
||||
//
|
||||
}
|
||||
export const BossList = [5701]
|
||||
export const StageDuration = 30
|
||||
export const SpawnBaseCd = 1.6
|
||||
export const SpawnMinCd = 0.5
|
||||
export const SpawnStageReduce = 0.08
|
||||
export const BossSpawnCd = 90
|
||||
export const SpawnPowerBias = 1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user