UNPKG

osu-utils

Version:

Basics Utils for osu projects

193 lines (175 loc) 7.04 kB
"use strict"; const rosu = require('rosu-pp-js'); /** * Wrapper class for rosu-pp-js to provide a simpler interface * for difficulty and performance calculations in osu-utils */ class RosuWrapper { constructor() { this.GameMode = rosu.GameMode; this.HitResultPriority = rosu.HitResultPriority; } /** * Parse a beatmap from content (string or Uint8Array) * @param {string|Uint8Array} content - Beatmap content * @returns {rosu.Beatmap} Parsed beatmap */ parseBeatmap(content) { return new rosu.Beatmap(content); } /** * Calculate difficulty attributes for a beatmap * @param {rosu.Beatmap} beatmap - Parsed beatmap * @param {Object} options - Difficulty calculation options * @returns {rosu.DifficultyAttributes} Difficulty attributes */ calculateDifficulty(beatmap, options = {}) { // Clean options to remove undefined values const cleanOptions = {}; for (const [key, value] of Object.entries(options)) { if (value !== undefined) { cleanOptions[key] = value; } } const difficulty = new rosu.Difficulty(cleanOptions); return difficulty.calculate(beatmap); } /** * Calculate performance attributes for a beatmap * @param {rosu.Beatmap|rosu.DifficultyAttributes} beatmapOrAttrs - Beatmap or difficulty attributes * @param {Object} options - Performance calculation options * @returns {rosu.PerformanceAttributes} Performance attributes */ calculatePerformance(beatmapOrAttrs, options = {}) { // Clean options to remove undefined values const cleanOptions = {}; for (const [key, value] of Object.entries(options)) { if (value !== undefined) { cleanOptions[key] = value; } } const performance = new rosu.Performance(cleanOptions); return performance.calculate(beatmapOrAttrs); } /** * Get strains for plotting difficulty over time * @param {rosu.Beatmap} beatmap - Parsed beatmap * @param {Object} options - Difficulty calculation options * @returns {rosu.Strains} Strain data */ getStrains(beatmap, options = {}) { // Clean options to remove undefined values const cleanOptions = {}; for (const [key, value] of Object.entries(options)) { if (value !== undefined) { cleanOptions[key] = value; } } const difficulty = new rosu.Difficulty(cleanOptions); return difficulty.strains(beatmap); } /** * Create a gradual difficulty calculator * @param {rosu.Beatmap} beatmap - Parsed beatmap * @param {Object} options - Difficulty calculation options * @returns {rosu.GradualDifficulty} Gradual difficulty calculator */ createGradualDifficulty(beatmap, options = {}) { const difficulty = new rosu.Difficulty(options); return difficulty.gradualDifficulty(beatmap); } /** * Create a gradual performance calculator * @param {rosu.Beatmap} beatmap - Parsed beatmap * @param {Object} options - Difficulty calculation options * @returns {rosu.GradualPerformance} Gradual performance calculator */ createGradualPerformance(beatmap, options = {}) { const difficulty = new rosu.Difficulty(options); return difficulty.gradualPerformance(beatmap); } /** * Build beatmap attributes with custom parameters * @param {Object} options - Beatmap attributes options * @returns {rosu.BeatmapAttributes} Beatmap attributes */ buildBeatmapAttributes(options = {}) { const builder = new rosu.BeatmapAttributesBuilder(options); return builder.build(); } /** * Helper method to convert mods string to integer * @param {string|number} mods - Mods as string or integer * @returns {number} Mods as integer */ parseMods(mods) { if (typeof mods === 'number') return mods; if (typeof mods === 'string') { // Simple mod parsing - can be extended let modInt = 0; const modMap = { 'NF': 1, 'EZ': 2, 'TD': 4, 'HD': 8, 'HR': 16, 'SD': 32, 'DT': 64, 'RX': 128, 'HT': 256, 'NC': 512, 'FL': 1024, 'AU': 2048, 'SO': 4096, 'AP': 8192, 'PF': 16384, 'K4': 32768, 'K5': 65536, 'K6': 131072, 'K7': 262144, 'K8': 524288, 'K9': 1048576, 'FI': 2097152, 'RD': 4194304, 'CN': 8388608, 'TP': 16777216, 'K1': 33554432, 'K3': 67108864, 'K2': 134217728, 'V2': 268435456, 'MR': 536870912 }; const modString = mods.toUpperCase().replace(/[^A-Z0-9]/g, ''); for (const [key, value] of Object.entries(modMap)) { if (modString.includes(key)) { modInt |= value; } } return modInt; } return 0; } /** * Helper method to format performance attributes for display * @param {rosu.PerformanceAttributes} attrs - Performance attributes * @returns {Object} Formatted attributes */ formatPerformance(attrs) { return { pp: attrs.pp, stars: attrs.difficulty.stars, aim: attrs.difficulty.aim || 0, speed: attrs.difficulty.speed || 0, flashlight: attrs.difficulty.flashlight || 0, sliderFactor: attrs.difficulty.sliderFactor || 0, ppAim: attrs.ppAim || 0, ppSpeed: attrs.ppSpeed || 0, ppFlashlight: attrs.ppFlashlight || 0, ppAccuracy: attrs.ppAccuracy || 0, effectiveMissCount: attrs.effectiveMissCount || 0, mode: attrs.difficulty.mode, maxCombo: attrs.difficulty.maxCombo }; } /** * Helper method to format difficulty attributes for display * @param {rosu.DifficultyAttributes} attrs - Difficulty attributes * @returns {Object} Formatted attributes */ formatDifficulty(attrs) { return { stars: attrs.stars, aim: attrs.aim || 0, speed: attrs.speed || 0, flashlight: attrs.flashlight || 0, sliderFactor: attrs.sliderFactor || 0, speedNoteCount: attrs.speedNoteCount || 0, aimDifficultStrainCount: attrs.aimDifficultStrainCount || 0, speedDifficultStrainCount: attrs.speedDifficultStrainCount || 0, nCircles: attrs.nCircles || 0, nSliders: attrs.nSliders || 0, nSpinners: attrs.nSpinners || 0, mode: attrs.mode, maxCombo: attrs.maxCombo }; } } module.exports = RosuWrapper;