feat(测试模式): 新增怪物技能测试覆盖功能并完善测试配置
修复怪物生成位置索引越界问题,通过对位置数组长度取模限制合法索引范围 新增测试模式专属的怪物技能覆盖逻辑,支持覆盖普攻及各类触发技能配置 扩展测试模式配置项,新增怪物生成数量、词缀配置以及全类型技能覆盖参数 优化 Rogue 关卡生成引擎,支持批量生成测试怪物并计算词缀属性加成
This commit is contained in:
@@ -194,6 +194,29 @@ export class Monster extends ecs.Entity {
|
|||||||
// 最终技能等级 = 初始技能等级 + 怪物等级增量,且下限为 0
|
// 最终技能等级 = 初始技能等级 + 怪物等级增量,且下限为 0
|
||||||
model.skills[skill.uuid] = { ...skill, lv: Math.max(0,skill.lv + mon_lv - 2), ccd: 0 };
|
model.skills[skill.uuid] = { ...skill, lv: Math.max(0,skill.lv + mon_lv - 2), ccd: 0 };
|
||||||
}
|
}
|
||||||
|
// 【测试模式专属】如果有覆盖技能配置则应用
|
||||||
|
const testSkills = (this as any)._testSkills;
|
||||||
|
if (testSkills) {
|
||||||
|
if (testSkills.skill !== undefined) {
|
||||||
|
// 如果是替换普攻,通常是修改 hero.skill 字段或从 model.skills 里清除并注入
|
||||||
|
model.skills = {};
|
||||||
|
if (testSkills.skill) {
|
||||||
|
model.skills[testSkills.skill.s_uuid] = {
|
||||||
|
uuid: testSkills.skill.s_uuid,
|
||||||
|
cd: testSkills.skill.cd ?? 0,
|
||||||
|
lv: 1,
|
||||||
|
ccd: 0,
|
||||||
|
overrides: testSkills.skill.overrides
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (testSkills.atking !== undefined) model.atking = testSkills.atking;
|
||||||
|
if (testSkills.atked !== undefined) model.atked = testSkills.atked;
|
||||||
|
if (testSkills.dead !== undefined) model.dead = testSkills.dead;
|
||||||
|
if (testSkills.fstart !== undefined) model.fstart = testSkills.fstart;
|
||||||
|
if (testSkills.fend !== undefined) model.fend = testSkills.fend;
|
||||||
|
}
|
||||||
|
|
||||||
// 缓存技能射程等派生数据,减少战斗帧内重复计算
|
// 缓存技能射程等派生数据,减少战斗帧内重复计算
|
||||||
model.updateSkillDistanceCache();
|
model.updateSkillDistanceCache();
|
||||||
|
|
||||||
|
|||||||
@@ -311,12 +311,16 @@ export class MissionMonCompComp extends CCComp {
|
|||||||
let scale = -1;
|
let scale = -1;
|
||||||
|
|
||||||
// 获取硬编码的占位点坐标,不再使用随机偏移
|
// 获取硬编码的占位点坐标,不再使用随机偏移
|
||||||
const basePos = MissionMonCompComp.MON_POSITIONS[posIndex];
|
const basePos = MissionMonCompComp.MON_POSITIONS[posIndex % MissionMonCompComp.MON_POSITIONS.length];
|
||||||
const spawnX = basePos.x;
|
const spawnX = basePos.x;
|
||||||
const landingY = basePos.y + (monData.isBoss ? 6 : 0);
|
const landingY = basePos.y + (monData.isBoss ? 6 : 0);
|
||||||
const spawnPos: Vec3 = v3(spawnX, landingY + MissionMonCompComp.MON_DROP_HEIGHT, 0);
|
const spawnPos: Vec3 = v3(spawnX, landingY + MissionMonCompComp.MON_DROP_HEIGHT, 0);
|
||||||
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999;
|
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999;
|
||||||
|
|
||||||
|
// 如果存在测试技能覆盖,则传递下去(修改 mon.load 逻辑或者通过预存)
|
||||||
|
// 为了避免侵入 Mon.ts 的原有逻辑,我们先预存
|
||||||
|
(mon as any)._testSkills = monData.testSkills;
|
||||||
|
|
||||||
mon.load(spawnPos, scale, monData.uuid, monData.isBoss, landingY, monLv, posIndex);
|
mon.load(spawnPos, scale, monData.uuid, monData.isBoss, landingY, monLv, posIndex);
|
||||||
|
|
||||||
// 设置渲染排序
|
// 设置渲染排序
|
||||||
|
|||||||
@@ -554,7 +554,21 @@ export const TestModeConfig = {
|
|||||||
/** 固定的测试怪物类型 */
|
/** 固定的测试怪物类型 */
|
||||||
monType: MonType.Melee,
|
monType: MonType.Melee,
|
||||||
/** 固定的测试怪物 UUID (如 6001 兽人战士) */
|
/** 固定的测试怪物 UUID (如 6001 兽人战士) */
|
||||||
monUuid: 6001
|
monUuid: 6001,
|
||||||
|
/** 测试模式中生成怪物是否携带特定词缀(为空则不带) */
|
||||||
|
affixes: [] as AffixType[],
|
||||||
|
/** 每次刷新的怪物数量 */
|
||||||
|
spawnCount: 1,
|
||||||
|
|
||||||
|
// ===== 附加技能测试覆盖配置 =====
|
||||||
|
/** 替换普攻技能(清除旧技能,添加新技能) */
|
||||||
|
skill: undefined as { s_uuid: number; cd?: number; overrides?: any } | undefined,
|
||||||
|
/** 覆盖触发技能(完全替换该类型的触发配置) */
|
||||||
|
atking: undefined as { s_uuid: number; t_num: number; overrides?: any }[] | undefined,
|
||||||
|
atked: undefined as { s_uuid: number; t_num: number; overrides?: any }[] | undefined,
|
||||||
|
dead: undefined as { s_uuid: number; t_num: number; overrides?: any }[] | undefined,
|
||||||
|
fstart: undefined as { s_uuid: number; t_num: number; overrides?: any }[] | undefined,
|
||||||
|
fend: undefined as { s_uuid: number; t_num: number; overrides?: any }[] | undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 向后兼容接口 ========================
|
// ======================== 向后兼容接口 ========================
|
||||||
@@ -606,6 +620,15 @@ export interface GeneratedMonster {
|
|||||||
affixes: AffixType[]
|
affixes: AffixType[]
|
||||||
isBoss: boolean
|
isBoss: boolean
|
||||||
spawnIndex: number
|
spawnIndex: number
|
||||||
|
/** 测试模式专属:临时覆盖怪物技能配置 */
|
||||||
|
testSkills?: {
|
||||||
|
skill?: { s_uuid: number; cd?: number; overrides?: any };
|
||||||
|
atking?: { s_uuid: number; t_num: number; overrides?: any }[];
|
||||||
|
atked?: { s_uuid: number; t_num: number; overrides?: any }[];
|
||||||
|
dead?: { s_uuid: number; t_num: number; overrides?: any }[];
|
||||||
|
fstart?: { s_uuid: number; t_num: number; overrides?: any }[];
|
||||||
|
fend?: { s_uuid: number; t_num: number; overrides?: any }[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 生成引擎 ========================
|
// ======================== 生成引擎 ========================
|
||||||
@@ -670,15 +693,37 @@ export class RogueSpawningEngine {
|
|||||||
// 测试模式拦截
|
// 测试模式拦截
|
||||||
if (TestModeConfig.enable) {
|
if (TestModeConfig.enable) {
|
||||||
const growth = 1 + (waveNumber - 1) * TestModeConfig.growthRatePerWave;
|
const growth = 1 + (waveNumber - 1) * TestModeConfig.growthRatePerWave;
|
||||||
return [{
|
|
||||||
uuid: TestModeConfig.monUuid,
|
// 提取词缀加成
|
||||||
type: TestModeConfig.monType,
|
const affixHpMul = 1.0 + TestModeConfig.affixes.reduce(
|
||||||
hp: Math.round(TestModeConfig.baseHp * growth),
|
(sum, a) => sum + (AffixConfigs[a].hpMultiplier - 1.0), 0
|
||||||
ap: Math.round(TestModeConfig.baseAp * growth),
|
);
|
||||||
affixes: [],
|
const affixApMul = 1.0 + TestModeConfig.affixes.reduce(
|
||||||
isBoss: false,
|
(sum, a) => sum + (AffixConfigs[a].apMultiplier - 1.0), 0
|
||||||
spawnIndex: 0
|
);
|
||||||
}];
|
|
||||||
|
const count = Math.max(1, TestModeConfig.spawnCount || 1);
|
||||||
|
const monsters: GeneratedMonster[] = [];
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
monsters.push({
|
||||||
|
uuid: TestModeConfig.monUuid,
|
||||||
|
type: TestModeConfig.monType,
|
||||||
|
hp: Math.round(TestModeConfig.baseHp * growth * affixHpMul),
|
||||||
|
ap: Math.round(TestModeConfig.baseAp * growth * affixApMul),
|
||||||
|
affixes: [...TestModeConfig.affixes],
|
||||||
|
isBoss: false,
|
||||||
|
spawnIndex: i,
|
||||||
|
testSkills: {
|
||||||
|
skill: TestModeConfig.skill,
|
||||||
|
atking: TestModeConfig.atking,
|
||||||
|
atked: TestModeConfig.atked,
|
||||||
|
dead: TestModeConfig.dead,
|
||||||
|
fstart: TestModeConfig.fstart,
|
||||||
|
fend: TestModeConfig.fend
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return monsters;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tier = Math.ceil(waveNumber / 3)
|
const tier = Math.ceil(waveNumber / 3)
|
||||||
|
|||||||
Reference in New Issue
Block a user