You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AdLike2048/assets/script/battle/config/MonsterWaveParser.ts

121 lines
4.0 KiB

3 weeks ago
import type { WaveDef, WavePortPlan, WaveSpawnSegment } from 'db://assets/mx/module/battle';
import type { ProjectLevelMonsterConfig } from './LevelMonsterConfig';
import {
calcChapterMonsterHp,
makeMonsterUnitDefId,
} from './MonsterHpCalculator';
export interface ParsedWaveSegment {
monsterId: number;
count: number;
difficultyRow: number;
}
export interface ParsedPortWaves {
port: number;
waves: ParsedWaveSegment[][];
}
export interface MergedWave {
ports: Array<{ port: number; segments: ParsedWaveSegment[] }>;
}
/**
* 解析 Chapter 出怪串
* - `|` 分隔波次
* - 波内 `;` 分隔顺序段(前段全部出完再出下段)
* - 每段 `id,count,difficultyRow`(逗号分隔,第三列可选,默认 1)
*/
export class MonsterWaveParser {
static parsePort(portStr: string | undefined | null): ParsedWaveSegment[][] {
if (!portStr || portStr.trim() === '') return [];
const waveStrs = portStr.split('|');
return waveStrs.map(w => this.parseWaveSegments(w));
}
static parseWaveSegments(waveStr: string): ParsedWaveSegment[] {
if (!waveStr || waveStr.trim() === '') return [];
const segments = waveStr.split(';');
const out: ParsedWaveSegment[] = [];
for (const seg of segments) {
const parsed = this.parseSegment(seg);
if (parsed) out.push(parsed);
}
return out;
}
static parseSegment(seg: string): ParsedWaveSegment | null {
const parts = seg.split(',').map(s => s.trim()).filter(Boolean);
if (parts.length < 2) return null;
const monsterId = parseInt(parts[0], 10);
const count = parseInt(parts[1], 10);
const difficultyRow = parts.length >= 3 ? parseInt(parts[2], 10) : 1;
if (isNaN(monsterId) || isNaN(count) || count <= 0) return null;
return {
monsterId,
count,
difficultyRow: isNaN(difficultyRow) ? 1 : difficultyRow,
};
}
/** 三口按波次索引对齐合并,每波内三口并行、口内顺序 */
static mergePorts(
port1: ParsedWaveSegment[][],
port2: ParsedWaveSegment[][],
port3: ParsedWaveSegment[][],
): MergedWave[] {
const max = Math.max(port1.length, port2.length, port3.length);
const merged: MergedWave[] = [];
for (let i = 0; i < max; i++) {
const ports: MergedWave['ports'] = [];
const addPort = (port: number, segments: ParsedWaveSegment[] | undefined) => {
if (segments?.length) ports.push({ port, segments });
};
addPort(1, port1[i]);
addPort(2, port2[i]);
addPort(3, port3[i]);
if (ports.length > 0) merged.push({ ports });
}
return merged;
}
static toWaveDefs(
merged: MergedWave[],
monsterConfig: ProjectLevelMonsterConfig,
spawnInterval: number,
firstWaveDelaySec: number,
subsequentIntervalSec: number,
waveCoin?: number[],
): WaveDef[] {
return merged.map((wave, idx) => {
const ports: WavePortPlan[] = wave.ports.map(({ port, segments }) => ({
port,
segments: segments.map(seg => this.toSpawnSegment(seg, port, monsterConfig, spawnInterval)),
}));
return {
id: `wave_${idx + 1}`,
prepTime: idx === 0 ? firstWaveDelaySec : subsequentIntervalSec,
rewardGold: waveCoin?.[idx] ?? 0,
ports,
};
});
}
private static toSpawnSegment(
seg: ParsedWaveSegment,
port: number,
monsterConfig: ProjectLevelMonsterConfig,
spawnInterval: number,
): WaveSpawnSegment {
return {
unitDefId: makeMonsterUnitDefId(seg.monsterId, port, seg.difficultyRow),
count: seg.count,
interval: spawnInterval,
difficultyRow: seg.difficultyRow,
maxHp: calcChapterMonsterHp(seg.monsterId, port, seg.difficultyRow, monsterConfig),
};
}
}