refactor(rogue): 调整Rogue模式怪物配置与战斗逻辑

1. 新增怪物出生站位配置
2. 调整单场战斗怪物数量为3只,支持0~2个Boss组合
3. 更新配置校验与怪物生成逻辑
4. 修复英雄预设体的尺寸与位置偏移
This commit is contained in:
pan
2026-08-20 17:19:24 +08:00
parent e18bcf2133
commit 965b04094d
3 changed files with 59 additions and 36 deletions

View File

@@ -250,7 +250,7 @@
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 0, "x": 0,
"y": -86.962, "y": -40.433,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -290,8 +290,8 @@
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
"width": 384, "width": 191.9,
"height": 384 "height": 192
}, },
"_anchorPoint": { "_anchorPoint": {
"__type__": "cc.Vec2", "__type__": "cc.Vec2",
@@ -981,8 +981,8 @@
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
"width": 150, "width": 80,
"height": 150 "height": 100
}, },
"_anchorPoint": { "_anchorPoint": {
"__type__": "cc.Vec2", "__type__": "cc.Vec2",
@@ -1090,12 +1090,12 @@
"_offset": { "_offset": {
"__type__": "cc.Vec2", "__type__": "cc.Vec2",
"x": 0, "x": 0,
"y": 75 "y": 50
}, },
"_size": { "_size": {
"__type__": "cc.Size", "__type__": "cc.Size",
"width": 150, "width": 80,
"height": 150 "height": 100
}, },
"_id": "" "_id": ""
}, },

View File

@@ -27,6 +27,11 @@ export enum FightSet {
HERO_ROW_FRONT_X = -200, // 前排(最靠右迎敌):坦克 / 战士 HERO_ROW_FRONT_X = -200, // 前排(最靠右迎敌):坦克 / 战士
HERO_ROW_MID_X = -260, // 中排:刺客 / 射手 HERO_ROW_MID_X = -260, // 中排:刺客 / 射手
HERO_ROW_BACK_X = -320, // 后排(最靠左):法师 / 辅助 HERO_ROW_BACK_X = -320, // 后排(最靠左):法师 / 辅助
/** 怪物三排出生/编队基准 xy 均为 BoxSet.GAME_LINE前排迎敌、中排、后排靠右 */
MON_ROW_FRONT_X = 200, // 前排(最靠左迎敌):近战 / 重型 / 近战Boss
MON_ROW_MID_X = 260, // 中排:远程 / 刺客 / 远程Boss
MON_ROW_BACK_X = 320, // 后排(最靠右):辅助 / 召唤师
WAVE_COIN_BASE = 7, // 回合金币基础奖励 WAVE_COIN_BASE = 7, // 回合金币基础奖励
WAVE_COIN_GROW = 2, // 回合金币递增值 WAVE_COIN_GROW = 2, // 回合金币递增值
WAVE_COIN_MAX = 17, // 回合金币最大基础奖励 WAVE_COIN_MAX = 17, // 回合金币最大基础奖励

View File

@@ -4,7 +4,7 @@
* *
* 游戏机制: * 游戏机制:
* - 整局只有一场战斗:开局英雄三次三选一后自动开战。 * - 整局只有一场战斗:开局英雄三次三选一后自动开战。
* - 怪物从 BossComboLibrary 按权重随机抽一组(1 Boss + 4 小怪), * - 怪物从 BossComboLibrary 按权重随机抽一组(固定 3 只怪0~2 Boss + 剩余小怪),
* 战斗开始瞬间一次全量登场X∈[100,320] 均匀铺开),双方立即互相攻击。 * 战斗开始瞬间一次全量登场X∈[100,320] 均匀铺开),双方立即互相攻击。
* - 怪物全灭 → 胜利结算;英雄全灭 → 失败结算;无回合递进、无分批刷怪。 * - 怪物全灭 → 胜利结算;英雄全灭 → 失败结算;无回合递进、无分批刷怪。
* *
@@ -18,7 +18,7 @@
* Boss 基础面板固定为初始英雄 2 倍强度BOSS_BASE_HP/BOSS_BASE_AP与小怪共用同一套系数 * Boss 基础面板固定为初始英雄 2 倍强度BOSS_BASE_HP/BOSS_BASE_AP与小怪共用同一套系数
* 场上 Boss 恰好为小怪 2 倍强度(如需额外压场可调 BOSS_HP_BONUS_MUL * 场上 Boss 恰好为小怪 2 倍强度(如需额外压场可调 BOSS_HP_BONUS_MUL
* 小怪基础面板与 1 级英雄持平(近战镜像 hp300/ap22/cd1.8),默认系数校准为 * 小怪基础面板与 1 级英雄持平(近战镜像 hp300/ap22/cd1.8),默认系数校准为
* 整场战斗3 英雄 vs 4 小怪 + 1 Boss)双方对砍 ≈ WAVE_DURATION(30s)。 * 整场战斗3 英雄 vs 3 只怪)双方对砍 ≈ WAVE_DURATION(30s)。
* 手感微调只改 MON_HP_MUL / MON_AP_MUL / BOSS_HP_BONUS_MUL 三个系数即可。 * 手感微调只改 MON_HP_MUL / MON_AP_MUL / BOSS_HP_BONUS_MUL 三个系数即可。
*/ */
@@ -31,9 +31,10 @@ import { FacSet } from "../common/config/GameSet";
/** 单场战斗时长基准供清屏速度评价BattleBannerComp 清屏奖励)等使用 */ /** 单场战斗时长基准供清屏速度评价BattleBannerComp 清屏奖励)等使用 */
export const WAVE_DURATION = 30; export const WAVE_DURATION = 30;
/** 怪物登场固定构成:1 个 Boss + 4 个小怪(一次全量登场,打完即整局结束) */ /** 怪物登场固定构成:单场战斗固定 3 只怪Boss 数量由组合配置决定0~2 个),
export const BOSS_COUNT = 1; * 剩余位置由小怪填充。例如3 小怪、1 Boss + 2 小怪、2 Boss + 1 小怪(最大难度) */
export const MINION_COUNT = 4; export const BOSS_COUNT = 2;
export const MINION_COUNT = 3;
/** 怪物血量可调系数:最终 hp = HeroInfo 基础hp × MON_HP_MUL。 /** 怪物血量可调系数:最终 hp = HeroInfo 基础hp × MON_HP_MUL。
* 2.6 = 校准后怪物总血量 / 3 英雄 30s 输出≈30 DPS双方对砍 ≈ 30s 分胜负 */ * 2.6 = 校准后怪物总血量 / 3 英雄 30s 输出≈30 DPS双方对砍 ≈ 30s 分胜负 */
@@ -104,11 +105,13 @@ export interface BossComboMinionSlot {
count: number; count: number;
} }
/** Boss 组合配置:一场战斗随机抽一组登场(1 Boss + minions 小怪 */ /** Boss 组合配置:一场战斗随机抽一组登场(固定 3 只怪Boss 数量由 bossCount 决定 */
export interface BossComboConfig { export interface BossComboConfig {
id: string; id: string;
name: string; name: string;
/** 小怪构成(合计数量建议等于 MINION_COUNT超出截断、不足自动补近战 */ /** 本场 Boss 数量0~2剩余位置由小怪填充 */
bossCount: number;
/** 小怪构成(合计数量建议等于 MINION_COUNT - bossCount超出截断、不足自动补近战 */
minions: BossComboMinionSlot[]; minions: BossComboMinionSlot[];
/** Boss 技能池 id引用 BossSkillPool可选 */ /** Boss 技能池 id引用 BossSkillPool可选 */
boss_skill_pool?: string[]; boss_skill_pool?: string[];
@@ -118,37 +121,46 @@ export interface BossComboConfig {
weight: number; weight: number;
} }
/** Boss 组合库(硬编码):每场战斗按权重随机抽一组 */ /** Boss 组合库(硬编码):每场战斗按权重随机抽一组,固定 3 只怪。
* 组合类型:
* - 3 小怪0 Boss均衡难度
* - 1 Boss + 2 小怪:标准难度
* - 2 Boss + 1 小怪:最大难度 */
export const BossComboLibrary: Record<string, BossComboConfig> = { export const BossComboLibrary: Record<string, BossComboConfig> = {
/** 3 小怪:均衡组合 */
rage_charge: { rage_charge: {
id: "rage_charge", name: "狂暴冲锋组", weight: 10, id: "rage_charge", name: "狂暴冲锋组", weight: 10, bossCount: 0,
minions: [{ type: MonType.Melee, count: 3 }, { type: MonType.Heavy, count: 1 }], minions: [{ type: MonType.Melee, count: 2 }, { type: MonType.Heavy, count: 1 }],
boss_skill_pool: ["boss_rage"],
}, },
iron_wall: { iron_wall: {
id: "iron_wall", name: "铁壁推进组", weight: 8, id: "iron_wall", name: "铁壁推进组", weight: 8, bossCount: 0,
minions: [{ type: MonType.Heavy, count: 2 }, { type: MonType.Support, count: 1 }, { type: MonType.Long, count: 1 }], minions: [{ type: MonType.Heavy, count: 2 }, { type: MonType.Support, count: 1 }],
boss_skill_pool: ["boss_iron"],
skill_pool: ["tough"], skill_pool: ["tough"],
}, },
doom_ritual: { doom_ritual: {
id: "doom_ritual", name: "毁灭仪式组", weight: 6, id: "doom_ritual", name: "毁灭仪式组", weight: 6, bossCount: 0,
minions: [{ type: MonType.Summoner, count: 1 }, { type: MonType.Long, count: 2 }, { type: MonType.Support, count: 1 }], minions: [{ type: MonType.Summoner, count: 1 }, { type: MonType.Long, count: 1 }, { type: MonType.Support, count: 1 }],
boss_skill_pool: ["boss_doom"],
skill_pool: ["warcry"], skill_pool: ["warcry"],
}, },
/** 1 Boss + 2 小怪:标准难度 */
assassin_raid: { assassin_raid: {
id: "assassin_raid", name: "刺客突袭组", weight: 7, id: "assassin_raid", name: "刺客突袭组", weight: 7, bossCount: 1,
minions: [{ type: MonType.Assassin, count: 2 }, { type: MonType.Melee, count: 2 }], minions: [{ type: MonType.Assassin, count: 1 }, { type: MonType.Melee, count: 1 }],
boss_skill_pool: ["boss_rage", "boss_doom"], boss_skill_pool: ["boss_rage", "boss_doom"],
skill_pool: ["berserk"], skill_pool: ["berserk"],
}, },
blood_legion: { blood_legion: {
id: "blood_legion", name: "吸血军团组", weight: 5, id: "blood_legion", name: "吸血军团组", weight: 5, bossCount: 1,
minions: [{ type: MonType.Melee, count: 2 }, { type: MonType.Long, count: 1 }, { type: MonType.Summoner, count: 1 }], minions: [{ type: MonType.Melee, count: 1 }, { type: MonType.Long, count: 1 }],
boss_skill_pool: ["boss_iron", "boss_doom"], boss_skill_pool: ["boss_iron", "boss_doom"],
skill_pool: ["leech"], skill_pool: ["leech"],
}, },
/** 2 Boss + 1 小怪:最大难度 */
twin_boss: {
id: "twin_boss", name: "双王降临", weight: 3, bossCount: 2,
minions: [{ type: MonType.Support, count: 1 }],
boss_skill_pool: ["boss_rage", "boss_iron", "boss_doom"],
},
}; };
/** /**
@@ -277,7 +289,7 @@ export const BossSkillPool: Record<string, MonSkillSet> = {
export function validateRogueConfig(): string[] { export function validateRogueConfig(): string[] {
const errors: string[] = []; const errors: string[] = [];
// 1. 校验组合引用与数量 // 1. 校验组合引用与数量(固定 3 只怪bossCount + minionTotal 必须等于 MINION_COUNT
for (const id in BossComboLibrary) { for (const id in BossComboLibrary) {
const combo = BossComboLibrary[id]; const combo = BossComboLibrary[id];
let minionTotal = 0; let minionTotal = 0;
@@ -287,8 +299,12 @@ export function validateRogueConfig(): string[] {
errors.push(`组合 ${id} 引用的怪物类型 ${slot.type}(${MonTypeName[slot.type] || "?"}) 在 MonList 中无可用 uuid`); errors.push(`组合 ${id} 引用的怪物类型 ${slot.type}(${MonTypeName[slot.type] || "?"}) 在 MonList 中无可用 uuid`);
} }
} }
if (minionTotal !== MINION_COUNT) { const totalMon = combo.bossCount + minionTotal;
errors.push(`组合 ${id} 小怪数量=${minionTotal} 与 MINION_COUNT=${MINION_COUNT} 不一致`); if (totalMon !== MINION_COUNT) {
errors.push(`组合 ${id} 总怪数=${totalMon}bossCount=${combo.bossCount} + minions=${minionTotal})与 MINION_COUNT=${MINION_COUNT} 不一致`);
}
if (combo.bossCount < 0 || combo.bossCount > BOSS_COUNT) {
errors.push(`组合 ${id} bossCount=${combo.bossCount} 超出允许范围 [0, ${BOSS_COUNT}]`);
} }
if (combo.skill_pool) { if (combo.skill_pool) {
for (const skillId of combo.skill_pool) { for (const skillId of combo.skill_pool) {
@@ -371,7 +387,7 @@ export const TestModeConfig = {
export class RogueSpawningEngine { export class RogueSpawningEngine {
/** /**
* 生成一场战斗的怪物列表:随机一组 Boss 组合(1 Boss + 4 小怪,属性走基础×系数)。 * 生成一场战斗的怪物列表:随机一组 Boss 组合(固定 3 只怪Boss 数量由 combo.bossCount 决定)。
* Boss 压队尾(登场时由 MissionMonComp 统一布位)。 * Boss 压队尾(登场时由 MissionMonComp 统一布位)。
* @returns 怪物列表小怪在前、Boss 在尾) * @returns 怪物列表小怪在前、Boss 在尾)
*/ */
@@ -383,6 +399,7 @@ export class RogueSpawningEngine {
const combo = pickBossCombo(); const combo = pickBossCombo();
const monsters: GeneratedMonster[] = []; const monsters: GeneratedMonster[] = [];
const bossCount = combo ? Math.max(0, Math.min(combo.bossCount, BOSS_COUNT)) : 0;
if (combo) { if (combo) {
for (const slot of combo.minions) { for (const slot of combo.minions) {
@@ -391,13 +408,14 @@ export class RogueSpawningEngine {
} }
} }
} }
// 组合库为空或数量不足兜底:补近战小怪至 MINION_COUNT // 组合库为空或数量不足兜底:补近战小怪至 MINION_COUNT - bossCount
while (monsters.length < MINION_COUNT) { const minionTarget = MINION_COUNT - bossCount;
while (monsters.length < minionTarget) {
monsters.push(this.makeMonster(MonType.Melee, monsters.length)); monsters.push(this.makeMonster(MonType.Melee, monsters.length));
} }
// Boss 压队尾 // Boss 压队尾
for (let i = 0; i < BOSS_COUNT; i++) { for (let i = 0; i < bossCount; i++) {
monsters.push(this.makeBoss()); monsters.push(this.makeBoss());
} }