export enum HitObjectType { HitCircle = 1, Slider = 2, Spinner = 8 } export type HitObject = { x: number; y: number; time: number; type: number; hitSound: number; objectParams: string; hitSample: string; currentCombo: number; }; export interface BeatmapDifficulty { hpDrainRate: number; circleSize: number; overralDifficulty: number; approachRate: number; sliderMultiplier: number; sliderTickRate: number; } export function parseBeatmapDifficulty(beatmap: string): BeatmapDifficulty { const lines = beatmap.split('\n'); let recording = false; const difficulty: Partial = {}; for (const line of lines) { if (line.trim() === '[Difficulty]') { recording = true; continue; } if (!recording) continue; if (line.startsWith('[')) break; const parts = line.split(':'); if (parts.length < 2) continue; switch (parts[0]) { case 'HPDrainRate': difficulty.hpDrainRate = parseFloat(parts[1]); break; case 'CircleSize': difficulty.circleSize = parseFloat(parts[1]); break; case 'OverallDifficulty': difficulty.overralDifficulty = parseFloat(parts[1]); break; case 'ApproachRate': difficulty.approachRate = parseFloat(parts[1]); break; case 'SliderMultiplier': difficulty.sliderMultiplier = parseFloat(parts[1]); break; case 'SliderTickRate': difficulty.sliderTickRate = parseFloat(parts[1]); break; } } return difficulty as BeatmapDifficulty; } export function parseHitObjects(beatmap: string): any[] { const lines = beatmap.split('\n'); let recording = false; const hitObjects = []; let currentCombo = 1; for (const line of lines) { if (line.trim() === '[HitObjects]') { recording = true; continue; } if (!recording) continue; if (line.startsWith('[')) break; const parts = line.split(','); if (parts.length < 5) continue; const type = parseInt(parts[3], 10); const isNewCombo = type & (1 << 2); // Bit at index 2 for new combo if (isNewCombo) { currentCombo = 1; // Reset combo } else { // If not the start of a new combo, increment the current combo currentCombo++; } const hitObject = { x: parseInt(parts[0], 10), y: parseInt(parts[1], 10), time: parseInt(parts[2], 10), type: getTypeFromFlag(type), hitSound: parseInt(parts[4], 10), objectParams: parts[5], hitSample: parts.length > 6 ? parts[6] : '0:0:0:0:', currentCombo: currentCombo }; if (isNewCombo) { // Reset currentCombo after assigning to hitObject if it's the start of a new combo currentCombo = 1; } hitObjects.push(hitObject); } return hitObjects; } function getTypeFromFlag(flag: number): HitObjectType | null { if (flag & HitObjectType.HitCircle) return HitObjectType.HitCircle; if (flag & HitObjectType.Slider) return HitObjectType.Slider; if (flag & HitObjectType.Spinner) return HitObjectType.Spinner; return null; }