JavaScript port of the current live version of the osu!mania ruleset.
Add a new dependency to your project via npm:
npm install osu-mania-stable
This ruleset is a part of osu-pp-calculator package. It's highly recommended to use existing package for performance calculation if you don't want to write a lot of boilerplate code for score simulation and beatmap downloading.
Before you can start using this ruleset library, you need to install osu-classes package as this ruleset based on it. Also you need to install osu-parsers or any other compatible beatmap parser that works with IBeatmap interface.
Any beatmap that implements IBeatmap interface can be converted to this ruleset. Unlike the game itself, you can convert beatmaps between different game modes, not just from osu!standard. This is possible due to the fact that hit objects from osu-classes keep their initial position taken from the .osu file. All beatmaps with applied ruleset keep the reference to the original beatmap, which allows you to repeat the process of conversion or apply different ruleset.
import { BeatmapDecoder, BeatmapEncoder } from 'osu-parsers';
import { ManiaRuleset } from 'osu-mania-stable';
const decoder = new BeatmapDecoder();
const encoder = new BeatmapEncoder();
const decodePath = 'path/to/your/decoding/file.osu';
const encodePath = 'path/to/your/encoding/file.osu';
const shouldParseSb = true;
// Get beatmap object.
const parsed = decoder.decodeFromPath(decodePath, shouldParseSb);
// Create a new osu!mania ruleset.
const ruleset = new ManiaRuleset();
// This will create a new shallow copy of a beatmap with applied osu!mania ruleset.
// This method implicitly applies mod combination of 0.
const maniaWithNoMod = ruleset.applyToBeatmap(parsed);
// Create mod combination and apply it to beatmap.
const mods = ruleset.createModCombination(1337);
const maniaWithMods = ruleset.applyToBeatmapWithMods(parsed, mods);
// It will write osu!mania beatmap with no mods to a file.
encoder.encodeToPath(encodePath, maniaWithNoMod);
// It will write osu!mania beatmap with applied mods to a file.
encoder.encodeToPath(encodePath, maniaWithMods);
// You can also write osu!mania beatmap object to a string.
const stringified = encoder.encodeToString(maniaWithMods);
import { BeatmapDecoder } from 'osu-parsers';
import { ManiaRuleset } from 'osu-mania-stable';
const decoder = new BeatmapDecoder();
const decodePath = 'path/to/your/decoding/file.osu';
// Get beatmap object.
const parsed = decoder.decodeFromPath(decodePath);
// Create a new osu!mania ruleset.
const ruleset = new ManiaRuleset();
// Create mod combination.
const mods = ruleset.createModCombination('DT');
// Create difficulty calculator for IBeatmap object.
const difficultyCalculator = ruleset.createDifficultyCalculator(parsed);
// You can pass any IBeatmap object to the difficulty calculator.
// Difficulty calculator will implicitly create a new beatmap with osu!mania ruleset.
const difficultyAttributes = difficultyCalculator.calculateWithMods(mods);
{
"maxCombo": 10406,
"mods": "DT",
"starRating": 11.433692671697369,
"greatHitWindow": 42
}
import { BeatmapDecoder } from 'osu-parsers';
import { ManiaRuleset } from 'osu-mania-stable';
const decoder = new BeatmapDecoder();
const decodePath = 'path/to/your/decoding/file.osu';
// Get beatmap object.
const parsed = decoder.decodeFromPath(decodePath);
// Create a new osu!mania ruleset.
const ruleset = new ManiaRuleset();
// Create mod combination and apply it to beatmap.
const mods = ruleset.createModCombination('HDHR');
const maniaWithMods = ruleset.applyToBeatmapWithMods(parsed, mods);
/**
* Any IBeatmap object can be used to create difficulty calculator.
* Difficulty calculator implicitly applies osu!mania ruleset with no mods.
*/
const difficultyCalculator1 = ruleset.createDifficultyCalculator(parsed);
const difficultyAttributes1 = difficultyCalculator1.calculate(); // no mods.
const moddedAttributes1 = difficultyCalculator1.calculateWithMods(mods); // with mods.
/**
* If you pass osu!mania beatmap then it will use its ruleset and mods.
*/
const difficultyCalculator2 = ruleset.createDifficultyCalculator(maniaWithMods);
const difficultyAttributes2 = difficultyCalculator2.calculate(); // with mods!
const moddedAttributes2 = difficultyCalculator2.calculateWithMods(mods); // the same as previous line.
/**
* You can also pass custom clock rate as the last parameter.
* It will be used instead of the original beatmap's clock rate.
*/
const customClockRate = 2;
const difficultyAttributes3 = difficultyCalculator2.calculate(customClockRate);
const moddedAttributes3 = difficultyCalculator2.calculateWithMods(mods, customClockRate);
Sometimes you may need to calculate difficulty of a beatmap gradually and return attributes on every step of calculation. This is useful for real time difficulty calculation when you update your values depending on the current hit object. This can be really slow and RAM heavy for long beatmaps because attributes are created every 400 ms of the beatmap (default strain step).
import { BeatmapDecoder } from 'osu-parsers';
import { ManiaRuleset } from 'osu-mania-stable';
const decoder = new BeatmapDecoder();
const decodePath = 'path/to/your/decoding/file.osu';
// Get beatmap object.
const parsed = decoder.decodeFromPath(decodePath);
// Create a new osu!mania ruleset.
const ruleset = new ManiaRuleset();
// Create mod combination and apply it to beatmap.
const mods = ruleset.createModCombination('NCHR');
// Create difficulty calculator for IBeatmap object.
const difficultyCalculator = ruleset.createDifficultyCalculator(parsed);
/**
* Calculate timed difficulty attributes of the beatmap.
*/
const difficultyAttributes1 = difficultyCalculator.calculateTimed();
const difficultyAttributes2 = difficultyCalculator.calculateTimedWithMods(mods);
/**
* Calculate time difficulty attributes with custom clock rate.
*/
const clockRate = 3;
const difficultyAttributes3 = difficultyCalculator.calculateTimed(mods, clockRate);
const difficultyAttributes4 = difficultyCalculator.calculateTimedWithMods(mods, clockRate);
This is a special case of gradual difficulty calculation. This is useful when you need to get difficulty attributes at a specific point of the beatmap. Unlike the previous method, this one returns difficulty attributes only once without huge memory allocations.
import { BeatmapDecoder } from 'osu-parsers';
import { ManiaRuleset } from 'osu-mania-stable';
const decoder = new BeatmapDecoder();
const decodePath = 'path/to/your/decoding/file.osu';
// Get beatmap object.
const parsed = decoder.decodeFromPath(decodePath);
// Create a new osu!mania ruleset.
const ruleset = new ManiaRuleset();
// Create mod combination and apply it to beatmap.
const mods = ruleset.createModCombination('EZHD');
// Create difficulty calculator for IBeatmap object.
const difficultyCalculator = ruleset.createDifficultyCalculator(parsed);
/**
* Get object count for partial difficulty calculation.
* This can be any number, but for example we will calculate first half of the beatmap.
*/
const totalObjects = Math.ceil(parsed.hitObjects.length / 2);
/**
* Calculate difficulty at the middle of the beatmap.
*/
const partialAttributes1 = difficultyCalculator.calculateAt(totalObjects);
const partialAttributes2 = difficultyCalculator.calculateWithModsAt(mods, totalObjects);
/**
* Calculate partial difficulty with custom clock rate.
*/
const clockRate = 1.2;
const partialAttributes3 = difficultyCalculator.calculateAt(totalObjects, clockRate);
const partialAttributes4 = difficultyCalculator.calculateWithModsAt(mods, totalObjects, clockRate);
Difficulty calculator can be used to calculate all attributes for every difficulty affecting modded combination. This can be time consuming for long beatmaps. All attributes are calculated inside generator function and returned as an iterator. Use JS spread syntax if you want to convert attributes to array.
import { BeatmapDecoder } from 'osu-parsers';
import { ManiaRuleset } from 'osu-mania-stable';
const decoder = new BeatmapDecoder();
const decodePath = 'path/to/your/decoding/file.osu';
// Get beatmap object.
const parsed = decoder.decodeFromPath(decodePath);
// Create a new osu!mania ruleset.
const ruleset = new ManiaRuleset();
// Create difficulty calculator for IBeatmap object.
const difficultyCalculator = ruleset.createDifficultyCalculator(parsed);
/**
* Calculate difficulty at the middle of the beatmap.
*/
for (const attributes of difficultyCalculator.calculateAll()) {
// Do your stuff with calculated attributes...
}
import { ScoreInfo } from 'osu-classes';
import { BeatmapDecoder } from 'osu-parsers';
import { ManiaRuleset } from 'osu-mania-stable';
const decoder = new BeatmapDecoder();
const decodePath = 'path/to/your/decoding/file.osu';
// Get beatmap object.
const parsed = decoder.decodeFromPath(decodePath);
// Create a new osu!mania ruleset.
const ruleset = new ManiaRuleset();
// Create mod combination and apply it to beatmap.
const mods = ruleset.createModCombination('DT');
const maniaBeatmap = ruleset.applyToBeatmapWithMods(parsed, mods);
// Create difficulty calculator for osu!mania beatmap.
const difficultyCalculator = ruleset.createDifficultyCalculator(maniaBeatmap);
// Calculate difficulty attributes.
const difficultyAttributes = difficultyCalculator.calculate();
// TIEFSEE (SOUND HOLIC vs. dj TAKA feat. YURiCa) [[7K] Despair]
// dressurf + NC 1450.8 pp.
const score = new ScoreInfo({
rulesetId: 3,
countGeki: 4545, // score.statistics.perfect
count300: 2073, // score.statistics.great
countKatu: 179, // score.statistics.good
count100: 20, // score.statistics.ok
count50: 22, // score.statistics.meh
countMiss: 50, // score.statistics.miss
mods,
});
// Create performance calculator for osu!mania ruleset.
const performanceCalculator = ruleset.createPerformanceCalculator(difficultyAttributes, score);
// Calculate performance attributes for a map.
const performanceAttributes = performanceCalculator.calculateAttributes();
// Calculate total performance for a map.
const totalPerformance = performanceCalculator.calculate();
{
"mods": "DT",
"totalPerformance": 1450.80114694415,
"difficultyPerformance": 181.35014336801876
}
All projects below are based on this code.
Auto-generated documentation is available here.
This project is being developed personally by me on pure enthusiasm. If you want to help with development or fix a problem, then feel free to create a new pull request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE for details.