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), }; } }