feat(怪物系统): 实现动态成长属性和改进刷怪逻辑
- 在Mon.ts中使用新的getMonAttr获取动态成长属性,包括速度 - 重构MissionMonComp.ts的刷怪逻辑,使用配置中的位置信息 - 重写RogueConfig.ts,实现基于波次和时间的动态属性成长系统 - 移除未使用的随机事件相关代码,简化刷怪队列结构
This commit is contained in:
@@ -70,15 +70,15 @@ export class Monster extends ecs.Entity {
|
|||||||
if(!model.is_boss){
|
if(!model.is_boss){
|
||||||
model.is_kalami = true;
|
model.is_kalami = true;
|
||||||
}
|
}
|
||||||
// 根据等级和类型获取怪物属性
|
// 根据等级和类型获取怪物属性(使用新的动态成长系统)
|
||||||
const {hp, mp, ap,def} = getMonAttr(lv, uuid, monType);
|
const {hp, mp, ap, def, speed} = getMonAttr(lv, uuid, monType);
|
||||||
// 初始化属性数组
|
// 初始化属性数组
|
||||||
model.Attrs = getAttrs();
|
model.Attrs = getAttrs();
|
||||||
model.hp = model.Attrs[Attrs.HP_MAX] = hp;
|
model.hp = model.Attrs[Attrs.HP_MAX] = hp;
|
||||||
model.mp = model.Attrs[Attrs.MP_MAX] = mp;
|
model.mp = model.Attrs[Attrs.MP_MAX] = mp;
|
||||||
model.Attrs[Attrs.DEF] = def;
|
model.Attrs[Attrs.DEF] = def;
|
||||||
model.Attrs[Attrs.AP] = ap;
|
model.Attrs[Attrs.AP] = ap;
|
||||||
model.Attrs[Attrs.SPEED] = hero.speed;
|
model.Attrs[Attrs.SPEED] = speed; // 使用成长后的速度
|
||||||
model.Attrs[Attrs.DIS] = hero.dis;
|
model.Attrs[Attrs.DIS] = hero.dis;
|
||||||
|
|
||||||
// ✅ 初始化技能数据(迁移到 HeroSkillsComp)
|
// ✅ 初始化技能数据(迁移到 HeroSkillsComp)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { MonStart } from "../common/config/heroSet";
|
|||||||
import { smc } from "../common/SingletonModuleComp";
|
import { smc } from "../common/SingletonModuleComp";
|
||||||
import { GameEvent } from "../common/config/GameEvent";
|
import { GameEvent } from "../common/config/GameEvent";
|
||||||
// 导入肉鸽配置
|
// 导入肉鸽配置
|
||||||
import { MonType, EventType, getStageMonConfigs} from "./RogueConfig";
|
import { getStageMonConfigs, MonType } from "./RogueConfig";
|
||||||
import { BuffConf } from "../common/config/SkillSet";
|
import { BuffConf } from "../common/config/SkillSet";
|
||||||
import { IndexSet } from "../common/config/GameSet";
|
import { IndexSet } from "../common/config/GameSet";
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
@@ -19,7 +19,7 @@ export class MissionMonCompComp extends CCComp {
|
|||||||
private MonQueue: Array<{
|
private MonQueue: Array<{
|
||||||
uuid: number,
|
uuid: number,
|
||||||
position: number,
|
position: number,
|
||||||
type: MonType
|
type: MonType,
|
||||||
level: number,
|
level: number,
|
||||||
buffs: BuffConf[]
|
buffs: BuffConf[]
|
||||||
}> = [];
|
}> = [];
|
||||||
@@ -29,7 +29,6 @@ export class MissionMonCompComp extends CCComp {
|
|||||||
private spawnCount: number = 0; // 召唤计数器
|
private spawnCount: number = 0; // 召唤计数器
|
||||||
private pauseInterval: number = 5.0; // 暂停间隔时间(5秒)
|
private pauseInterval: number = 5.0; // 暂停间隔时间(5秒)
|
||||||
private isPausing: boolean = false; // 是否正在暂停
|
private isPausing: boolean = false; // 是否正在暂停
|
||||||
private currentEvent: EventType | null = null; // 当前关卡的随机事件
|
|
||||||
private eventProcessed: boolean = false; // 事件是否已处理
|
private eventProcessed: boolean = false; // 事件是否已处理
|
||||||
/** 全局生成顺序计数器,用于层级管理 */
|
/** 全局生成顺序计数器,用于层级管理 */
|
||||||
private globalSpawnOrder: number = 0;
|
private globalSpawnOrder: number = 0;
|
||||||
@@ -58,10 +57,7 @@ export class MissionMonCompComp extends CCComp {
|
|||||||
if(!smc.mission.play||smc.mission.pause) return
|
if(!smc.mission.play||smc.mission.pause) return
|
||||||
|
|
||||||
// 处理随机事件
|
// 处理随机事件
|
||||||
if (this.currentEvent && !this.eventProcessed) {
|
|
||||||
|
|
||||||
this.eventProcessed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理刷怪队列
|
// 处理刷怪队列
|
||||||
if (this.MonQueue.length > 0 && !this.isSpawning) {
|
if (this.MonQueue.length > 0 && !this.isSpawning) {
|
||||||
@@ -126,17 +122,17 @@ export class MissionMonCompComp extends CCComp {
|
|||||||
|
|
||||||
// 为每个怪物配置生成怪物
|
// 为每个怪物配置生成怪物
|
||||||
monsConf.forEach((mon: any, index: number) => {
|
monsConf.forEach((mon: any, index: number) => {
|
||||||
const { uuid, type,level, buffs } = mon;
|
const { uuid, type, level, buffs, position } = mon;
|
||||||
|
|
||||||
// 位置循环使用 (0-4)
|
// 使用配置中的位置,如果没有则使用索引
|
||||||
const position = index % 5;
|
const spawnPosition = position !== undefined ? position : (index % 5);
|
||||||
|
|
||||||
this.addToStageSpawnQueue(
|
this.addToStageSpawnQueue(
|
||||||
uuid,
|
uuid,
|
||||||
position,
|
spawnPosition,
|
||||||
type,
|
type,
|
||||||
level, // 默认等级1
|
level,
|
||||||
buffs // 强度倍率
|
buffs
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -147,7 +143,7 @@ export class MissionMonCompComp extends CCComp {
|
|||||||
private addToStageSpawnQueue(
|
private addToStageSpawnQueue(
|
||||||
uuid: number,
|
uuid: number,
|
||||||
position: number,
|
position: number,
|
||||||
type: MonType,
|
type: MonType = MonType.NORMAL,
|
||||||
level: number = 1,
|
level: number = 1,
|
||||||
buffs: BuffConf[] = []
|
buffs: BuffConf[] = []
|
||||||
) {
|
) {
|
||||||
@@ -182,7 +178,7 @@ export class MissionMonCompComp extends CCComp {
|
|||||||
private addMonster(
|
private addMonster(
|
||||||
uuid: number = 1001,
|
uuid: number = 1001,
|
||||||
i: number = 0,
|
i: number = 0,
|
||||||
monType: MonType = MonType.NORMAL,
|
monType: number = 0,
|
||||||
lv: number = 1,
|
lv: number = 1,
|
||||||
buffs: BuffConf[] = []
|
buffs: BuffConf[] = []
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,21 @@
|
|||||||
* - 支持程序化关卡生成逻辑,每一关的怪物组合、数量和强度应随关卡进度递增而变化
|
* - 支持程序化关卡生成逻辑,每一关的怪物组合、数量和强度应随关卡进度递增而变化
|
||||||
* - 支持随机事件系统
|
* - 支持随机事件系统
|
||||||
*
|
*
|
||||||
|
*
|
||||||
|
* 3. 全局动态 Scaling 算法既然是 15 分钟单局,属性不应一成不变。
|
||||||
|
* 建议引入 Wave_Factor (波次因子):
|
||||||
|
* 公式建议: Current_Stat = Base_Stat * (1 + (time / 60) * 0.15) ^ Growth_Type
|
||||||
|
* •HP 成长: 设为指数级 (Growth_Type = 1.2)。后期怪物血量会呈几何倍数增加,适配『神装英雄』。
|
||||||
|
* •AP 成长: 设为线性 (Growth_Type = 1.0)。保证怪物能击穿后期护盾,但不会一击必杀。
|
||||||
|
* •Speed 成长: 设为对数级 (Growth_Type = 0.5)。防止后期怪物速度过快导致画面瞬移。Donny DBM/Getty Images
|
||||||
|
*
|
||||||
|
*
|
||||||
* @author 游戏开发团队
|
* @author 游戏开发团队
|
||||||
* @version 2.0 增强版
|
* @version 2.0 增强版
|
||||||
* @date 2025-10-19
|
* @date 2025-10-19
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getMonList, HeroInfo } from "../common/config/heroSet";
|
import { HeroInfo } from "../common/config/heroSet";
|
||||||
import { BuffConf } from "../common/config/SkillSet";
|
|
||||||
|
|
||||||
|
|
||||||
// 精英怪物配置表
|
// 精英怪物配置表
|
||||||
export const EliteMons = [ 5201, 5202, 5203, 5213 ];
|
export const EliteMons = [ 5201, 5202, 5203, 5213 ];
|
||||||
@@ -29,61 +36,111 @@ export const Mons={
|
|||||||
/**
|
/**
|
||||||
* 怪物类型枚举
|
* 怪物类型枚举
|
||||||
*/
|
*/
|
||||||
export enum MonType { NORMAL = 0, ELITE = 1, BOSS = 2 }
|
export enum MonType {
|
||||||
export const EliteStage =[5,10,15,20,25]
|
NORMAL = 0, // 普通怪物
|
||||||
export const BossStage=[30,40,50,60,70,80,90,100]
|
ELITE = 1, // 精英怪物
|
||||||
|
BOSS = 2 // Boss怪物
|
||||||
export enum EventType {
|
|
||||||
TREASURE = 1, // 额外奖励
|
|
||||||
TRAP =2, // 陷阱伤害
|
|
||||||
BUFF = 3, // 临时增益效果
|
|
||||||
DEBUFF = 4 // 临时减益效果
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关卡生怪物相关配置
|
* 怪物配置接口
|
||||||
*/
|
*/
|
||||||
export const StageRule = {
|
export interface IMonsConfig {
|
||||||
MonsNum: 5, // 关卡中默认怪物数量
|
uuid: number; // 怪物ID
|
||||||
LimitMonNum: 10, // 30关以后是极限模式,怪物数量
|
type: MonType; // 怪物类型
|
||||||
/** 额外怪物出现概率(在固定5个怪物基础上,有概率多刷1个) */
|
level: number; // 等级
|
||||||
extraMonsterRate: 0.3, // 30%概率出现第6个怪物
|
position?: number; // 位置(可选)
|
||||||
/** 事件怪物出现概率(5个怪物中有1个替换为事件怪) */
|
buffs?: any[]; // buff列表(可选)
|
||||||
eventMonsterRate: 0.25, // 25%概率出现事件怪物
|
|
||||||
/** 特殊属性怪物出现概率(5个怪物中有怪物携带特殊属性) */
|
|
||||||
specialAttributeRate: 0.4, // 40%概率出现特殊属性怪物
|
|
||||||
/** 特殊属性怪物数量范围 */
|
|
||||||
specialAttributeCount: { min: 1, max: 2 } // 出现时,1-2个怪物会有特殊属性
|
|
||||||
};
|
|
||||||
|
|
||||||
interface IMonsConfig {
|
|
||||||
/** 怪物波次 */
|
|
||||||
uuid: number; // 怪物ID
|
|
||||||
/** 怪物数量 */
|
|
||||||
buff: BuffConf[]; //附加属性
|
|
||||||
/** 怪物等级 */
|
|
||||||
level: number; // 怪物等级
|
|
||||||
/** 是否为精英怪物 */
|
|
||||||
monType:MonType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MonAttrSet={
|
/**
|
||||||
[MonType.NORMAL]:{ HP_MAX:1.1, AP:1.05, MP:1.1, DEF:1.05,},
|
* 怪物属性接口
|
||||||
[MonType.ELITE]: { HP_MAX:2, AP:1.1, MP:1.1, DEF:1.1,},
|
*/
|
||||||
[MonType.BOSS]: { HP_MAX:5, AP:2, MP:5, DEF:2,},
|
export interface MonAttrs {
|
||||||
|
hp: number;
|
||||||
|
mp: number;
|
||||||
|
ap: number;
|
||||||
|
def: number;
|
||||||
|
speed: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MonBuffSet={
|
/**
|
||||||
|
* 成长类型枚举
|
||||||
|
*/
|
||||||
|
enum GrowthType {
|
||||||
|
EXPONENTIAL = 1.2, // 指数级 - HP
|
||||||
|
LINEAR = 1.0, // 线性 - AP
|
||||||
|
LOGARITHMIC = 0.5 // 对数级 - Speed
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMonAttr=(lv:number,uuid:number,MonType:MonType)=>{
|
/**
|
||||||
let mon=HeroInfo[uuid]
|
* 计算波次因子
|
||||||
let hp=mon.hp*lv*MonAttrSet[MonType].HP_MAX
|
* @param stage 当前波次
|
||||||
let mp=mon.mp*lv*MonAttrSet[MonType].MP
|
* @param timeInSeconds 游戏进行时间(秒)
|
||||||
let ap=mon.ap*lv*MonAttrSet[MonType].AP
|
* @returns 波次因子 (0-1之间,15分钟时达到最大)
|
||||||
let def=mon.def*lv*MonAttrSet[MonType].DEF
|
*/
|
||||||
return {hp:hp,mp:mp,ap:ap,def:def}
|
function calculateWaveFactor(stage: number, timeInSeconds: number = 0): number {
|
||||||
|
const MAX_GAME_TIME = 15 * 60; // 15分钟 = 900秒
|
||||||
|
const effectiveTime = timeInSeconds || (stage * 30); // 如果没有时间数据,用波次估算(每波30秒)
|
||||||
|
const factor = Math.min(effectiveTime / MAX_GAME_TIME, 1.0);
|
||||||
|
return factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用成长公式到基础属性
|
||||||
|
* @param baseStat 基础属性值
|
||||||
|
* @param waveFactor 波次因子 (0-1)
|
||||||
|
* @param growthType 成长类型
|
||||||
|
* @returns 成长后的属性值
|
||||||
|
*/
|
||||||
|
function applyGrowthFormula(baseStat: number, waveFactor: number, growthType: GrowthType): number {
|
||||||
|
// 公式: Current_Stat = Base_Stat * (1 + waveFactor * 0.15) ^ Growth_Type
|
||||||
|
const growthMultiplier = Math.pow(1 + waveFactor * 0.15, growthType);
|
||||||
|
return Math.floor(baseStat * growthMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取怪物动态成长属性
|
||||||
|
* @param stage 当前波次
|
||||||
|
* @param uuid 怪物ID
|
||||||
|
* @param monType 怪物类型
|
||||||
|
* @param timeInSeconds 游戏进行时间(秒)
|
||||||
|
* @returns 怪物属性
|
||||||
|
*/
|
||||||
|
export function getMonAttr(stage: number, uuid: number, monType: MonType = MonType.NORMAL, timeInSeconds: number = 0): MonAttrs {
|
||||||
|
const baseMonster = HeroInfo[uuid];
|
||||||
|
if (!baseMonster) {
|
||||||
|
console.warn(`[RogueConfig] 未找到怪物ID: ${uuid}`);
|
||||||
|
return { hp: 100, mp: 100, ap: 10, def: 0, speed: 100 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算波次因子
|
||||||
|
const waveFactor = calculateWaveFactor(stage, timeInSeconds);
|
||||||
|
|
||||||
|
// 根据怪物类型应用额外的倍率
|
||||||
|
let typeMultiplier = 1.0;
|
||||||
|
if (monType === MonType.ELITE) {
|
||||||
|
typeMultiplier = 2.0; // 精英怪2倍属性
|
||||||
|
} else if (monType === MonType.BOSS) {
|
||||||
|
typeMultiplier = 5.0; // Boss 5倍属性
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用不同的成长类型
|
||||||
|
const hp = applyGrowthFormula(baseMonster.hp, waveFactor, GrowthType.EXPONENTIAL) * typeMultiplier;
|
||||||
|
const ap = applyGrowthFormula(baseMonster.ap, waveFactor, GrowthType.LINEAR) * typeMultiplier;
|
||||||
|
const speed = applyGrowthFormula(baseMonster.speed, waveFactor, GrowthType.LOGARITHMIC);
|
||||||
|
|
||||||
|
// MP和DEF使用线性成长
|
||||||
|
const mp = applyGrowthFormula(baseMonster.mp, waveFactor, GrowthType.LINEAR);
|
||||||
|
const def = applyGrowthFormula(baseMonster.def, waveFactor, GrowthType.LINEAR) * typeMultiplier;
|
||||||
|
|
||||||
|
return {
|
||||||
|
hp: Math.floor(hp),
|
||||||
|
mp: Math.floor(mp),
|
||||||
|
ap: Math.floor(ap),
|
||||||
|
def: Math.floor(def),
|
||||||
|
speed: Math.floor(speed)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,83 +151,39 @@ export const getMonAttr=(lv:number,uuid:number,MonType:MonType)=>{
|
|||||||
export function getStageMonConfigs(stage: number): IMonsConfig[] {
|
export function getStageMonConfigs(stage: number): IMonsConfig[] {
|
||||||
const monsterConfigs: IMonsConfig[] = [];
|
const monsterConfigs: IMonsConfig[] = [];
|
||||||
|
|
||||||
// 确定基础怪物数量
|
// 基础怪物列表(从heroset.ts中获取)
|
||||||
let baseMonsterCount = StageRule.MonsNum;
|
const normalMons = [5201, 5301, 5401, 5501, 5601, 5602, 5603, 5604];
|
||||||
|
const eliteMons = [5701];
|
||||||
|
|
||||||
// 判断是否为Boss波次
|
// 根据波次生成怪物配置
|
||||||
const isBossStage = BossStage.includes(stage);
|
// 波次越高,怪物数量越多,精英怪物出现概率越高
|
||||||
|
const baseCount = 5 + Math.floor(stage / 2); // 基础数量每2波增加1
|
||||||
|
const eliteChance = Math.min(stage * 0.05, 0.3); // 精英怪概率最高30%
|
||||||
|
const bossWave = stage % 10 === 0; // 每10波出Boss
|
||||||
|
|
||||||
// 判断是否为精英波次
|
if (bossWave && stage > 0) {
|
||||||
const isEliteStage = EliteStage.includes(stage);
|
// Boss波
|
||||||
|
|
||||||
// 如果是Boss波次,增加一个Boss怪物
|
|
||||||
if (isBossStage) {
|
|
||||||
// 从Boss怪物列表中随机选择一个
|
|
||||||
const bossUUID = BossMons[Math.floor(Math.random() * BossMons.length)] || 5201;
|
|
||||||
monsterConfigs.push({
|
monsterConfigs.push({
|
||||||
uuid: bossUUID,
|
uuid: 5701,
|
||||||
buff: [],
|
type: MonType.BOSS,
|
||||||
level: stage, // Boss等级等于波次
|
level: stage
|
||||||
monType: MonType.BOSS
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// 普通波
|
||||||
|
for (let i = 0; i < baseCount; i++) {
|
||||||
|
// 随机决定是否生成精英怪
|
||||||
|
const isElite = Math.random() < eliteChance;
|
||||||
|
const monList = isElite ? eliteMons : normalMons;
|
||||||
|
const randomUuid = monList[Math.floor(Math.random() * monList.length)];
|
||||||
|
|
||||||
// Boss波次减少普通怪物数量
|
|
||||||
baseMonsterCount = Math.max(1, baseMonsterCount - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是精英波次,增加精英怪物
|
|
||||||
if (isEliteStage) {
|
|
||||||
// 添加1-2个精英怪物
|
|
||||||
const eliteCount = isBossStage ? 1 : Math.floor(Math.random() * 2) + 1;
|
|
||||||
for (let i = 0; i < eliteCount; i++) {
|
|
||||||
const eliteUUID = EliteMons[Math.floor(Math.random() * EliteMons.length)] || 5201;
|
|
||||||
monsterConfigs.push({
|
monsterConfigs.push({
|
||||||
uuid: eliteUUID,
|
uuid: randomUuid,
|
||||||
buff: [],
|
type: isElite ? MonType.ELITE : MonType.NORMAL,
|
||||||
level: stage, // 精英等级等于波次
|
level: stage,
|
||||||
monType: MonType.ELITE
|
position: i % 5
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 精英波次减少普通怪物数量
|
|
||||||
baseMonsterCount = Math.max(1, baseMonsterCount - eliteCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加普通怪物
|
|
||||||
const remainingCount = baseMonsterCount;
|
|
||||||
for (let i = 0; i < remainingCount; i++) {
|
|
||||||
// 从普通怪物列表中随机选择一个
|
|
||||||
const normalMonsters = getMonList();
|
|
||||||
const normalUUID = normalMonsters.length > 0
|
|
||||||
? normalMonsters[Math.floor(Math.random() * normalMonsters.length)]
|
|
||||||
: 5201;
|
|
||||||
|
|
||||||
monsterConfigs.push({
|
|
||||||
uuid: normalUUID,
|
|
||||||
buff: [],
|
|
||||||
level: stage, // 普通怪物等级等于波次
|
|
||||||
monType: MonType.NORMAL
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断是否生成额外怪物
|
|
||||||
if (Math.random() < StageRule.extraMonsterRate) {
|
|
||||||
const normalMonsters = getMonList();
|
|
||||||
const extraUUID = normalMonsters.length > 0
|
|
||||||
? normalMonsters[Math.floor(Math.random() * normalMonsters.length)]
|
|
||||||
: 5201;
|
|
||||||
|
|
||||||
monsterConfigs.push({
|
|
||||||
uuid: extraUUID,
|
|
||||||
buff: [],
|
|
||||||
level: stage,
|
|
||||||
monType: MonType.NORMAL
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return monsterConfigs;
|
return monsterConfigs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user