osu-utils
Version:
Basics Utils for osu projects
193 lines (175 loc) • 7.04 kB
JavaScript
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;
;