@rian8337/osu-rebalance-difficulty-calculator
Version:
A module for calculating osu!standard beatmap difficulty and performance value with respect to the latest osu!lazer difficulty and performance algorithm.
1,497 lines (1,456 loc) • 51.4 kB
TypeScript
import { MapStats, Mod, PlaceableHitObject, Modes, Beatmap, HitObject, Accuracy } from '@rian8337/osu-base';
/**
* An evaluator for calculating aim skill.
*
* This class should be considered an "evaluating" class and not persisted.
*/
declare abstract class AimEvaluator {
protected static readonly wideAngleMultiplier: number;
protected static readonly acuteAngleMultiplier: number;
protected static readonly sliderMultiplier: number;
protected static readonly velocityChangeMultiplier: number;
/**
* Calculates the bonus of wide angles.
*/
protected static calculateWideAngleBonus(angle: number): number;
/**
* Calculates the bonus of acute angles.
*/
protected static calculateAcuteAngleBonus(angle: number): number;
}
/**
* The base of calculation options.
*/
interface CalculationOptions {
/**
* Custom map statistics to apply custom speed multiplier as well as old statistics.
*/
stats?: MapStats;
}
/**
* Holds data that can be used to calculate performance points.
*/
interface DifficultyAttributes {
/**
* The mods which were applied to the beatmap.
*/
mods: Mod[];
/**
* The combined star rating of all skills.
*/
starRating: number;
/**
* The maximum achievable combo.
*/
maxCombo: number;
/**
* The difficulty corresponding to the aim skill.
*/
aimDifficulty: number;
/**
* The difficulty corresponding to the flashlight skill.
*/
flashlightDifficulty: number;
/**
* The number of clickable objects weighted by difficulty.
*
* Related to speed/tap difficulty.
*/
speedNoteCount: number;
/**
* Describes how much of aim difficulty is contributed to by hitcircles or sliders.
*
* A value closer to 1 indicates most of aim difficulty is contributed by hitcircles.
*
* A value closer to 0 indicates most of aim difficulty is contributed by sliders.
*/
sliderFactor: number;
/**
* The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
*
* Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
*/
approachRate: number;
/**
* The perceived overall difficulty inclusive of rate-adjusting mods (DT/HT/etc), based on osu!standard judgement.
*
* Rate-adjusting mods don't directly affect the overall difficulty value, but have a perceived effect as a result of adjusting audio timing.
*/
overallDifficulty: number;
/**
* The number of hitcircles in the beatmap.
*/
hitCircleCount: number;
/**
* The number of sliders in the beatmap.
*/
sliderCount: number;
/**
* The number of spinners in the beatmap.
*/
spinnerCount: number;
}
/**
* Represents options for difficulty calculation.
*/
interface DifficultyCalculationOptions extends CalculationOptions {
/**
* The modifications to apply.
*/
mods?: Mod[];
}
/**
* Represents an osu!standard hit object with difficulty calculation values.
*/
declare class DifficultyHitObject {
/**
* The underlying hitobject.
*/
readonly object: PlaceableHitObject;
/**
* The index of this hitobject in the list of all hitobjects.
*
* This is one less than the actual index of the hitobject in the beatmap.
*/
index: number;
/**
* The preempt time of the hitobject.
*/
baseTimePreempt: number;
/**
* Adjusted preempt time of the hitobject, taking speed multiplier into account.
*/
timePreempt: number;
/**
* The fade in time of the hitobject.
*/
timeFadeIn: number;
/**
* The aim strain generated by the hitobject if sliders are considered.
*/
aimStrainWithSliders: number;
/**
* The aim strain generated by the hitobject if sliders are not considered.
*/
aimStrainWithoutSliders: number;
/**
* The tap strain generated by the hitobject.
*
* This is also used for osu!standard as opposed to "speed strain".
*/
tapStrain: number;
/**
* The tap strain generated by the hitobject if `strainTime` isn't modified by
* OD. This is used in three-finger detection.
*/
originalTapStrain: number;
/**
* The rhythm multiplier generated by the hitobject. This is used to alter tap strain.
*/
rhythmMultiplier: number;
/**
* The rhythm strain generated by the hitobject.
*/
rhythmStrain: number;
/**
* The flashlight strain generated by the hitobject if sliders are considered.
*/
flashlightStrain: number;
/**
* The flashlight strain generated by the hitobject if sliders are not considered.
*/
flashlightStrainWithoutSliders: number;
/**
* The visual strain generated by the hitobject if sliders are considered.
*/
visualStrain: number;
/**
* The visual strain generated by the hitobject if sliders are not considered.
*/
visualStrainWithoutSliders: number;
/**
* The normalized distance from the "lazy" end position of the previous hitobject to the start position of this hitobject.
*
* The "lazy" end position is the position at which the cursor ends up if the previous hitobject is followed with as minimal movement as possible (i.e. on the edge of slider follow circles).
*/
lazyJumpDistance: number;
/**
* The normalized shortest distance to consider for a jump between the previous hitobject and this hitobject.
*
* This is bounded from above by `lazyJumpDistance`, and is smaller than the former if a more natural path is able to be taken through the previous hitobject.
*
* Suppose a linear slider - circle pattern. Following the slider lazily (see: `lazyJumpDistance`) will result in underestimating the true end position of the slider as being closer towards the start position.
* As a result, `lazyJumpDistance` overestimates the jump distance because the player is able to take a more natural path by following through the slider to its end,
* such that the jump is felt as only starting from the slider's true end position.
*
* Now consider a slider - circle pattern where the circle is stacked along the path inside the slider.
* In this case, the lazy end position correctly estimates the true end position of the slider and provides the more natural movement path.
*/
minimumJumpDistance: number;
/**
* The time taken to travel through `minimumJumpDistance`, with a minimum value of 25ms.
*/
minimumJumpTime: number;
/**
* The normalized distance between the start and end position of this hitobject.
*/
travelDistance: number;
/**
* The time taken to travel through `travelDistance`, with a minimum value of 25ms for sliders.
*/
travelTime: number;
/**
* Angle the player has to take to hit this hitobject.
*
* Calculated as the angle between the circles (current-2, current-1, current).
*/
angle: number | null;
/**
* The amount of milliseconds elapsed between this hitobject and the last hitobject.
*/
deltaTime: number;
/**
* The amount of milliseconds elapsed since the start time of the previous hitobject, with a minimum of 25ms.
*/
strainTime: number;
/**
* Adjusted start time of the hitobject, taking speed multiplier into account.
*/
startTime: number;
/**
* Adjusted end time of the hitobject, taking speed multiplier into account.
*/
endTime: number;
/**
* The note density of the hitobject.
*/
noteDensity: number;
/**
* The overlapping factor of the hitobject.
*
* This is used to scale visual skill.
*/
overlappingFactor: number;
/**
* Adjusted velocity of the hitobject, taking speed multiplier into account.
*/
velocity: number;
/**
* Other hitobjects in the beatmap, including this hitobject.
*/
private readonly hitObjects;
/**
* @param object The underlying hitobject.
* @param hitObjects All difficulty hitobjects in the processed beatmap.
*/
constructor(object: PlaceableHitObject, hitObjects: DifficultyHitObject[]);
/**
* Gets the difficulty hitobject at a specific index with respect to the current
* difficulty hitobject's index.
*
* Will return `null` if the index is out of range.
*
* @param backwardsIndex The index to move backwards for.
* @returns The difficulty hitobject at the index with respect to the current
* difficulty hitobject's index, `null` if the index is out of range.
*/
previous(backwardsIndex: number): DifficultyHitObject | null;
/**
* Gets the difficulty hitobject at a specific index with respect to the current
* difficulty hitobject's index.
*
* Will return `null` if the index is out of range.
*
* @param forwardsIndex The index to move forwards for.
* @returns The difficulty hitobject at the index with respect to the current
* difficulty hitobject's index, `null` if the index is out of range.
*/
next(forwardsIndex: number): DifficultyHitObject | null;
/**
* Calculates the opacity of the hitobject at a given time.
*
* @param time The time to calculate the hitobject's opacity at.
* @param isHidden Whether Hidden mod is used.
* @param mode The gamemode to calculate the opacity for.
* @returns The opacity of the hitobject at the given time.
*/
opacityAt(time: number, isHidden: boolean, mode: Modes): number;
/**
* Determines whether this hitobject is considered overlapping with the hitobject before it.
*
* Keep in mind that "overlapping" in this case is overlapping to the point where both hitobjects
* can be hit with just a single tap in osu!droid.
*
* @param considerDistance Whether to consider the distance between both hitobjects.
* @returns Whether the hitobject is considered overlapping.
*/
isOverlapping(considerDistance: boolean): boolean;
}
/**
* A bare minimal abstract skill for fully custom skill implementations.
*/
declare abstract class Skill {
/**
* The mods that this skill processes.
*/
protected readonly mods: Mod[];
constructor(mods: Mod[]);
/**
* Calculates the strain value of a hitobject and stores the value in it.
* This value is affected by previously processed objects.
*
* @param current The hitobject to process.
*/
abstract process(current: DifficultyHitObject): void;
/**
* Returns the calculated difficulty value representing all hitobjects that have been processed up to this point.
*/
abstract difficultyValue(): number;
}
/**
* Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
* and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
*/
declare abstract class StrainSkill extends Skill {
/**
* The strain of currently calculated hitobject.
*/
protected currentStrain: number;
/**
* The current section's strain peak.
*/
protected currentSectionPeak: number;
/**
* Strain peaks are stored here.
*/
readonly strainPeaks: number[];
/**
* The number of sections with the highest strains, which the peak strain reductions will apply to.
* This is done in order to decrease their impact on the overall difficulty of the map for this skill.
*/
protected abstract readonly reducedSectionCount: number;
/**
* The baseline multiplier applied to the section with the biggest strain.
*/
protected abstract readonly reducedSectionBaseline: number;
/**
* Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
*/
protected abstract readonly skillMultiplier: number;
/**
* Determines how quickly strain decays for the given skill.
*
* For example, a value of 0.15 indicates that strain decays to 15% of its original value in one second.
*/
protected abstract readonly strainDecayBase: number;
private readonly sectionLength;
private currentSectionEnd;
private isFirstObject;
/**
* Calculates the strain value of a hitobject and stores the value in it. This value is affected by previously processed objects.
*
* @param current The hitobject to process.
*/
process(current: DifficultyHitObject): void;
/**
* Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
*/
saveCurrentPeak(): void;
/**
* Calculates strain decay for a specified time frame.
*
* @param ms The time frame to calculate.
*/
protected strainDecay(ms: number): number;
/**
* Calculates the strain value at a hitobject.
*/
protected abstract strainValueAt(current: DifficultyHitObject): number;
/**
* Saves the current strain to a hitobject.
*/
protected abstract saveToHitObject(current: DifficultyHitObject): void;
/**
* Sets the initial strain level for a new section.
*
* @param offset The beginning of the new section in milliseconds, adjusted by speed multiplier.
* @param current The current hitobject.
*/
private startNewSectionFrom;
}
/**
* Represents the strain peaks of various calculated difficulties.
*/
interface StrainPeaks {
/**
* The strain peaks of aim difficulty if sliders are considered.
*/
aimWithSliders: number[];
/**
* The strain peaks of aim difficulty if sliders are not considered.
*/
aimWithoutSliders: number[];
/**
* The strain peaks of speed difficulty.
*/
speed: number[];
/**
* The strain peaks of flashlight difficulty.
*/
flashlight: number[];
}
/**
* The base of a difficulty calculator.
*/
declare abstract class DifficultyCalculator {
/**
* The calculated beatmap.
*/
readonly beatmap: Beatmap;
/**
* The difficulty objects of the beatmap.
*/
readonly objects: DifficultyHitObject[];
/**
* The modifications applied.
*/
mods: Mod[];
/**
* The total star rating of the beatmap.
*/
total: number;
/**
* The map statistics of the beatmap after modifications are applied.
*/
stats: MapStats;
/**
* The strain peaks of various calculated difficulties.
*/
readonly strainPeaks: StrainPeaks;
/**
* Holds data that can be used to calculate performance points.
*/
abstract readonly attributes: DifficultyAttributes;
protected readonly sectionLength: number;
protected abstract readonly difficultyMultiplier: number;
protected abstract readonly mode: Modes;
/**
* Constructs a new instance of the calculator.
*
* @param beatmap The beatmap to calculate. This beatmap will be deep-cloned to prevent reference changes.
*/
constructor(beatmap: Beatmap);
/**
* Calculates the star rating of the specified beatmap.
*
* The beatmap is analyzed in chunks of `sectionLength` duration.
* For each chunk the highest hitobject strains are added to
* a list which is then collapsed into a weighted sum, much
* like scores are weighted on a user's profile.
*
* For subsequent chunks, the initial max strain is calculated
* by decaying the previous hitobject's strain until the
* beginning of the new chunk.
*
* @param options Options for the difficulty calculation.
*/
calculate(options?: DifficultyCalculationOptions): this;
/**
* Generates difficulty hitobjects for this calculator.
*/
generateDifficultyHitObjects(): void;
/**
* Performs some pre-processing before proceeding with difficulty calculation.
*/
protected preProcess(): void;
/**
* Calculates the skills provided.
*
* @param skills The skills to calculate.
*/
protected calculateSkills(...skills: StrainSkill[]): void;
/**
* Calculates the total star rating of the beatmap and stores it in this instance.
*/
abstract calculateTotal(): void;
/**
* Calculates every star rating of the beatmap and stores it in this instance.
*/
abstract calculateAll(): void;
/**
* Returns a string representative of the class.
*/
abstract toString(): string;
/**
* Creates skills to be calculated.
*/
protected abstract createSkills(): StrainSkill[];
/**
* Populates the stored difficulty attributes with necessary data.
*/
protected populateDifficultyAttributes(): void;
/**
* Calculates the star rating value of a difficulty.
*
* @param difficulty The difficulty to calculate.
*/
protected starValue(difficulty: number): number;
/**
* Calculates the base performance value of a difficulty rating.
*
* @param rating The difficulty rating.
*/
protected basePerformanceValue(rating: number): number;
}
/**
* A converter used to convert normal hitobjects into difficulty hitobjects.
*/
declare class DifficultyHitObjectCreator {
/**
* The threshold for small circle buff for osu!droid.
*/
private readonly DROID_CIRCLESIZE_BUFF_THRESHOLD;
/**
* The threshold for small circle buff for osu!standard.
*/
private readonly PC_CIRCLESIZE_BUFF_THRESHOLD;
/**
* The gamemode this creator is creating for.
*/
private mode;
/**
* The base normalized radius of hitobjects.
*/
private readonly normalizedRadius;
private maximumSliderRadius;
private readonly assumedSliderRadius;
private readonly minDeltaTime;
/**
* Generates difficulty hitobjects for difficulty calculation.
*/
generateDifficultyObjects(params: {
objects: readonly HitObject[];
circleSize: number;
mods: Mod[];
speedMultiplier: number;
mode: Modes;
preempt?: number;
}): DifficultyHitObject[];
/**
* Calculates a slider's cursor position.
*/
private calculateSliderCursorPosition;
/**
* Gets the scaling factor of a radius.
*
* @param radius The radius to get the scaling factor from.
*/
private getScalingFactor;
/**
* Returns the end cursor position of a hitobject.
*/
private getEndCursorPosition;
private applyToOverlappingFactor;
}
/**
* Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
* and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
*/
declare abstract class DroidSkill extends StrainSkill {
/**
* The bonus multiplier that is given for a sequence of notes of equal difficulty.
*/
protected abstract readonly starsPerDouble: number;
difficultyValue(): number;
}
/**
* Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
*/
declare class DroidAim extends DroidSkill {
protected readonly skillMultiplier: number;
protected readonly strainDecayBase: number;
protected readonly reducedSectionCount: number;
protected readonly reducedSectionBaseline: number;
protected readonly starsPerDouble: number;
private readonly withSliders;
constructor(mods: Mod[], withSliders: boolean);
/**
* @param current The hitobject to calculate.
*/
protected strainValueAt(current: DifficultyHitObject): number;
/**
* @param current The hitobject to save to.
*/
protected saveToHitObject(current: DifficultyHitObject): void;
}
/**
* An evaluator for calculating osu!droid Aim skill.
*/
declare abstract class DroidAimEvaluator extends AimEvaluator {
protected static readonly wideAngleMultiplier: number;
protected static readonly sliderMultiplier: number;
protected static readonly velocityChangeMultiplier: number;
/**
* Spacing threshold for a single hitobject spacing.
*/
private static readonly SINGLE_SPACING_THRESHOLD;
private static readonly minSpeedBonus;
/**
* Evaluates the difficulty of aiming the current object, based on:
*
* - cursor velocity to the current object,
* - angle difficulty,
* - sharp velocity increases,
* - and slider difficulty.
*
* @param current The current object.
* @param withSliders Whether to take slider difficulty into account.
*/
static evaluateDifficultyOf(current: DifficultyHitObject, withSliders: boolean): number;
/**
* Calculates the aim strain of a hitobject.
*/
private static aimStrainOf;
/**
* Calculates the movement strain of a hitobject.
*/
private static movementStrainOf;
}
/**
* Holds data that can be used to calculate osu!droid performance points.
*/
interface DroidDifficultyAttributes extends DifficultyAttributes {
/**
* The difficulty corresponding to the tap skill.
*/
tapDifficulty: number;
/**
* The difficulty corresponding to the rhythm skill.
*/
rhythmDifficulty: number;
/**
* The difficulty corresponding to the visual skill.
*/
visualDifficulty: number;
/**
* The number of clickable objects weighted by difficulty.
*
* Related to aim difficulty.
*/
aimNoteCount: number;
/**
* Describes how much of flashlight difficulty is contributed to by hitcircles or sliders.
*
* A value closer to 1 indicates most of flashlight difficulty is contributed by hitcircles.
*
* A value closer to 0 indicates most of flashlight difficulty is contributed by sliders.
*/
flashlightSliderFactor?: number;
/**
* Describes how much of flashlight difficulty is contributed to by hitcircles or sliders.
*
* A value closer to 1 indicates most of flashlight difficulty is contributed by hitcircles.
*
* A value closer to 0 indicates most of flashlight difficulty is contributed by sliders.
*/
visualSliderFactor?: number;
}
/**
* A difficulty calculator for osu!droid gamemode.
*/
declare class DroidDifficultyCalculator extends DifficultyCalculator {
/**
* The aim star rating of the beatmap.
*/
aim: number;
/**
* The tap star rating of the beatmap.
*/
tap: number;
/**
* The rhythm star rating of the beatmap.
*/
rhythm: number;
/**
* The flashlight star rating of the beatmap.
*/
flashlight: number;
/**
* The visual star rating of the beatmap.
*/
visual: number;
readonly attributes: DroidDifficultyAttributes;
protected readonly difficultyMultiplier: number;
protected readonly mode: Modes;
/**
* Calculates the aim star rating of the beatmap and stores it in this instance.
*/
calculateAim(): void;
/**
* Calculates the tap star rating of the beatmap and stores it in this instance.
*/
calculateTap(): void;
/**
* Calculates the rhythm star rating of the beatmap and stores it in this instance.
*/
calculateRhythm(): void;
/**
* Calculates the flashlight star rating of the beatmap and stores it in this instance.
*/
calculateFlashlight(): void;
/**
* Calculates the visual star rating of the beatmap and stores it in this instance.
*/
calculateVisual(): void;
calculateTotal(): void;
calculateAll(): void;
toString(): string;
protected preProcess(): void;
protected createSkills(): DroidSkill[];
/**
* Called after aim skill calculation.
*
* @param aimSkill The aim skill that considers sliders.
* @param aimSkillWithoutSliders The aim skill that doesn't consider sliders.
*/
private postCalculateAim;
/**
* Calculates aim-related attributes.
*/
private calculateAimAttributes;
/**
* Called after tap skill calculation.
*
* @param tapSkill The tap skill.
*/
private postCalculateTap;
/**
* Calculates speed-related attributes.
*/
private calculateSpeedAttributes;
/**
* Called after rhythm skill calculation.
*
* @param rhythmSkill The rhythm skill.
*/
private postCalculateRhythm;
/**
* Called after flashlight skill calculation.
*
* @param flashlightSkill The flashlight skill that considers sliders.
* @param flashlightSkillWithoutSliders The flashlight skill that doesn't consider sliders.
*/
private postCalculateFlashlight;
/**
* Called after visual skill calculation.
*
* @param visualSkillWithSliders The visual skill that considers sliders.
* @param visualSkillWithoutSliders The visual skill that doesn't consider sliders.
*/
private postCalculateVisual;
}
/**
* Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
*/
declare class DroidFlashlight extends DroidSkill {
protected readonly skillMultiplier: number;
protected readonly strainDecayBase: number;
protected readonly reducedSectionCount: number;
protected readonly reducedSectionBaseline: number;
protected readonly starsPerDouble: number;
private readonly isHidden;
private readonly withSliders;
constructor(mods: Mod[], withSliders?: boolean);
/**
* @param current The hitobject to calculate.
*/
protected strainValueAt(current: DifficultyHitObject): number;
protected saveToHitObject(current: DifficultyHitObject): void;
}
/**
* An evaluator for calculating flashlight skill.
*
* This class should be considered an "evaluating" class and not persisted.
*/
declare abstract class FlashlightEvaluator {
protected static readonly maxOpacityBonus: number;
protected static readonly hiddenBonus: number;
protected static readonly minVelocity: number;
protected static readonly sliderMultiplier: number;
protected static readonly minAngleMultiplier: number;
}
/**
* An evaluator for calculating osu!droid Flashlight skill.
*/
declare abstract class DroidFlashlightEvaluator extends FlashlightEvaluator {
/**
* Evaluates the difficulty of memorizing and hitting the current object, based on:
*
* - distance between a number of previous objects and the current object,
* - the visual opacity of the current object,
* - the angle made by the current object,
* - length and speed of the current object (for sliders),
* - and whether Hidden mod is enabled.
*
* @param current The current object.
* @param isHiddenMod Whether the Hidden mod is enabled.
* @param withSliders Whether to take slider difficulty into account. Defaults to `true`.
*/
static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean, withSliders?: boolean): number;
}
/**
* Represents options for performance calculation.
*/
interface PerformanceCalculationOptions {
/**
* The maximum combo achieved in the score.
*/
combo?: number;
/**
* The accuracy achieved in the score.
*/
accPercent?: Accuracy | number;
/**
* The amount of misses achieved in the score.
*/
miss?: number;
/**
* The tap penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
*/
tapPenalty?: number;
/**
* The aim slider cheese penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
*/
aimSliderCheesePenalty?: number;
/**
* The flashlight slider cheese penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
*/
flashlightSliderCheesePenalty?: number;
/**
* The visual slider cheese penalty to apply for penalized scores. Only used when using `DroidPerformanceCalculator`.
*/
visualSliderCheesePenalty?: number;
}
/**
* The base class of performance calculators.
*/
declare abstract class PerformanceCalculator {
/**
* The overall performance value.
*/
total: number;
/**
* The calculated accuracy.
*/
computedAccuracy: Accuracy;
/**
* The difficulty attributes that is being calculated.
*/
abstract readonly difficultyAttributes: DifficultyAttributes;
/**
* Penalty for combo breaks.
*/
protected comboPenalty: number;
/**
* The global multiplier to be applied to the final performance value.
*
* This is being adjusted to keep the final value scaled around what it used to be when changing things.
*/
protected abstract finalMultiplier: number;
/**
* The gamemode to calculate for.
*/
protected abstract readonly mode: Modes;
/**
* The amount of misses that are filtered out from sliderbreaks.
*/
protected effectiveMissCount: number;
/**
* Nerf factor used for nerfing beatmaps with very likely dropped sliderends.
*/
protected sliderNerfFactor: number;
/**
* Calculates the performance points of the beatmap.
*
* @param options Options for performance calculation.
* @returns The current instance.
*/
calculate(options?: PerformanceCalculationOptions): this;
/**
* Returns a string representative of the class.
*/
abstract toString(): string;
/**
* Calculates all values that will be used for calculating the total
* performance value of the beatmap and stores them in this instance.
*/
protected abstract calculateValues(): void;
/**
* Calculates the total performance value of the beatmap and stores it in this instance.
*/
protected abstract calculateTotalValue(): void;
/**
* The total hits that can be done in the beatmap.
*/
protected get totalHits(): number;
/**
* The total hits that were successfully done.
*/
protected get totalSuccessfulHits(): number;
/**
* Calculates the base performance value of a star rating.
*/
protected baseValue(stars: number): number;
/**
* Processes given options for usage in performance calculation.
*
* @param options Options for performance calculation.
*/
protected handleOptions(options?: PerformanceCalculationOptions): void;
/**
* Calculates the amount of misses + sliderbreaks from combo.
*/
private calculateEffectiveMissCount;
}
/**
* A performance points calculator that calculates performance points for osu!droid gamemode.
*/
declare class DroidPerformanceCalculator extends PerformanceCalculator {
/**
* The aim performance value.
*/
aim: number;
/**
* The tap performance value.
*/
tap: number;
/**
* The accuracy performance value.
*/
accuracy: number;
/**
* The flashlight performance value.
*/
flashlight: number;
/**
* The visual performance value.
*/
visual: number;
/**
* The penalty used to penalize the tap performance value.
*
* Can be properly obtained by analyzing the replay associated with the score.
*/
get tapPenalty(): number;
/**
* The estimated deviation of the score.
*/
get deviation(): number;
/**
* The estimated tap deviation of the score.
*/
get tapDeviation(): number;
/**
* The penalty used to penalize the aim performance value.
*
* Can be properly obtained by analyzing the replay associated with the score.
*/
get aimSliderCheesePenalty(): number;
/**
* The penalty used to penalize the flashlight performance value.
*
* Can be properly obtained by analyzing the replay associated with the score.
*/
get flashlightSliderCheesePenalty(): number;
/**
* The penalty used to penalize the visual performance value.
*
* Can be properly obtained by analyzing the replay associated with the score.
*/
get visualSliderCheesePenalty(): number;
readonly difficultyAttributes: DroidDifficultyAttributes;
protected finalMultiplier: number;
protected readonly mode: Modes;
private _aimSliderCheesePenalty;
private _flashlightSliderCheesePenalty;
private _visualSliderCheesePenalty;
private _tapPenalty;
private _deviation;
private _tapDeviation;
/**
* @param difficultyAttributes The difficulty attributes to calculate.
*/
constructor(difficultyAttributes: DroidDifficultyAttributes);
/**
* Applies a tap penalty value to this calculator.
*
* The tap and total performance value will be recalculated afterwards.
*
* @param value The tap penalty value. Must be greater than or equal to 1.
*/
applyTapPenalty(value: number): void;
/**
* Applies an aim slider cheese penalty value to this calculator.
*
* The aim and total performance value will be recalculated afterwards.
*
* @param value The slider cheese penalty value. Must be between than 0 and 1.
*/
applyAimSliderCheesePenalty(value: number): void;
/**
* Applies a flashlight slider cheese penalty value to this calculator.
*
* The flashlight and total performance value will be recalculated afterwards.
*
* @param value The slider cheese penalty value. Must be between 0 and 1.
*/
applyFlashlightSliderCheesePenalty(value: number): void;
/**
* Applies a visual slider cheese penalty value to this calculator.
*
* The visual and total performance value will be recalculated afterwards.
*
* @param value The slider cheese penalty value. Must be between 0 and 1.
*/
applyVisualSliderCheesePenalty(value: number): void;
protected calculateValues(): void;
protected calculateTotalValue(): void;
protected handleOptions(options?: PerformanceCalculationOptions): void;
/**
* Calculates the aim performance value of the beatmap.
*/
private calculateAimValue;
/**
* Calculates the tap performance value of the beatmap.
*/
private calculateTapValue;
/**
* Calculates the accuracy performance value of the beatmap.
*/
private calculateAccuracyValue;
/**
* Calculates the flashlight performance value of the beatmap.
*/
private calculateFlashlightValue;
/**
* Calculates the visual performance value of the beatmap.
*/
private calculateVisualValue;
/**
* Estimates the player's tap deviation based on the OD, number of circles and sliders,
* and number of 300s, 100s, 50s, and misses, assuming the player's mean hit error is 0.
*
* The estimation is consistent in that two SS scores on the same map
* with the same settings will always return the same deviation.
*
* Sliders are treated as circles with a 50 hit window.
*
* Misses are ignored because they are usually due to misaiming, and 50s
* are grouped with 100s since they are usually due to misreading.
*
* Inaccuracies are capped to the number of circles in the map.
*/
private calculateDeviation;
/**
* Does the same as {@link calculateDeviation}, but only for notes and inaccuracies that are relevant to tap difficulty.
*
* Treats all difficult speed notes as circles, so this method can sometimes return a lower deviation than {@link calculateDeviation}.
* This is fine though, since this method is only used to scale tap pp.
*/
private calculateTapDeviation;
toString(): string;
}
/**
* Represents the skill required to properly follow a beatmap's rhythm.
*/
declare class DroidRhythm extends DroidSkill {
protected readonly skillMultiplier: number;
protected readonly reducedSectionCount: number;
protected readonly reducedSectionBaseline: number;
protected readonly strainDecayBase: number;
protected readonly starsPerDouble: number;
private currentRhythm;
private readonly hitWindow;
constructor(mods: Mod[], overallDifficulty: number);
protected strainValueAt(current: DifficultyHitObject): number;
protected saveToHitObject(current: DifficultyHitObject): void;
}
/**
* An evaluator for calculating rhythm skill.
*
* This class should be considered an "evaluating" class and not persisted.
*/
declare abstract class RhythmEvaluator {
protected static readonly rhythmMultiplier: number;
protected static readonly historyTimeMax: number;
}
/**
* An evaluator for calculating osu!droid Rhythm skill.
*/
declare abstract class DroidRhythmEvaluator extends RhythmEvaluator {
/**
* Calculates a rhythm multiplier for the difficulty of the tap associated
* with historic data of the current object.
*
* @param current The current object.
* @param greatWindow The great hit window of the current object.
*/
static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
}
/**
* Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
*/
declare class DroidTap extends DroidSkill {
protected readonly skillMultiplier: number;
protected readonly reducedSectionCount: number;
protected readonly reducedSectionBaseline: number;
protected readonly strainDecayBase: number;
protected readonly starsPerDouble: number;
private currentTapStrain;
private currentOriginalTapStrain;
private readonly greatWindow;
constructor(mods: Mod[], overallDifficulty: number);
/**
* @param current The hitobject to calculate.
*/
protected strainValueAt(current: DifficultyHitObject): number;
/**
* @param current The hitobject to save to.
*/
protected saveToHitObject(current: DifficultyHitObject): void;
}
/**
* An evaluator for calculating speed or tap skill.
*
* This class should be considered an "evaluating" class and not persisted.
*/
declare abstract class SpeedEvaluator {
protected static readonly minSpeedBonus: number;
}
/**
* An evaluator for calculating osu!droid tap skill.
*/
declare abstract class DroidTapEvaluator extends SpeedEvaluator {
/**
* Evaluates the difficulty of tapping the current object, based on:
*
* - time between pressing the previous and current object,
* - distance between those objects,
* - and how easily they can be cheesed.
*
* @param current The current object.
* @param greatWindow The great hit window of the current object.
* @param considerCheesability Whether to consider cheesability.
*/
static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number, considerCheesability: boolean): number;
}
/**
* Represents the skill required to read every object in the map.
*/
declare class DroidVisual extends DroidSkill {
protected readonly starsPerDouble: number;
protected readonly reducedSectionCount: number;
protected readonly reducedSectionBaseline: number;
protected readonly skillMultiplier: number;
protected readonly strainDecayBase: number;
private readonly isHidden;
private readonly withsliders;
constructor(mods: Mod[], withSliders?: boolean);
protected strainValueAt(current: DifficultyHitObject): number;
protected saveToHitObject(current: DifficultyHitObject): void;
}
/**
* An evaluator for calculating osu!droid Visual skill.
*/
declare abstract class DroidVisualEvaluator {
/**
* Evaluates the difficulty of reading the current object, based on:
*
* - note density of the current object,
* - overlapping factor of the current object,
* - the preempt time of the current object,
* - the visual opacity of the current object,
* - the velocity of the current object if it's a slider,
* - past objects' velocity if they are sliders,
* - and whether the Hidden mod is enabled.
*
* @param current The current object.
* @param isHiddenMod Whether the Hidden mod is enabled.
* @param withSliders Whether to take slider difficulty into account. Defaults to `true`.
*/
static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean, withSliders?: boolean): number;
}
/**
* Used to processes strain values of difficulty hitobjects, keep track of strain levels caused by the processed objects
* and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
*/
declare abstract class OsuSkill extends StrainSkill {
/**
* The default multiplier applied to the final difficulty value after all other calculations.
*
* May be overridden via {@link difficultyMultiplier}.
*/
static readonly defaultDifficultyMultiplier: number;
/**
* The final multiplier to be applied to the final difficulty value after all other calculations.
*/
protected readonly difficultyMultiplier: number;
/**
* The weight by which each strain value decays.
*/
protected abstract readonly decayWeight: number;
difficultyValue(): number;
}
/**
* Holds data that can be used to calculate osu!standard performance points.
*/
interface OsuDifficultyAttributes extends DifficultyAttributes {
/**
* The difficulty corresponding to the speed skill.
*/
speedDifficulty: number;
}
/**
* A difficulty calculator for osu!standard gamemode.
*/
declare class OsuDifficultyCalculator extends DifficultyCalculator {
/**
* The aim star rating of the beatmap.
*/
aim: number;
/**
* The speed star rating of the beatmap.
*/
speed: number;
/**
* The flashlight star rating of the beatmap.
*/
flashlight: number;
readonly attributes: OsuDifficultyAttributes;
protected readonly difficultyMultiplier: number;
protected readonly mode: Modes;
/**
* Calculates the aim star rating of the beatmap and stores it in this instance.
*/
calculateAim(): void;
/**
* Calculates the speed star rating of the beatmap and stores it in this instance.
*/
calculateSpeed(): void;
/**
* Calculates the flashlight star rating of the beatmap and stores it in this instance.
*/
calculateFlashlight(): void;
calculateTotal(): void;
calculateAll(): void;
toString(): string;
protected preProcess(): void;
protected createSkills(): OsuSkill[];
/**
* Called after aim skill calculation.
*
* @param aimSkill The aim skill that considers sliders.
* @param aimSkillWithoutSliders The aim skill that doesn't consider sliders.
*/
private postCalculateAim;
/**
* Called after speed skill calculation.
*
* @param speedSkill The speed skill.
*/
private postCalculateSpeed;
/**
* Calculates speed-related attributes.
*/
private calculateSpeedAttributes;
/**
* Called after flashlight skill calculation.
*
* @param flashlightSkill The flashlight skill.
*/
private postCalculateFlashlight;
}
/**
* A difficulty calculator that calculates for both osu!droid and osu!standard gamemode.
*/
declare class MapStars {
/**
* The osu!droid difficulty calculator of the beatmap.
*/
readonly droid: DroidDifficultyCalculator;
/**
* The osu!standard difficulty calculator of the beatmap.
*/
readonly osu: OsuDifficultyCalculator;
/**
* Constructs this instance and calculates the given beatmap's osu!droid and osu!standard difficulty.
*
* @param beatmap The beatmap to calculate.
* @param options Options for the difficulty calculation.
*/
constructor(beatmap: Beatmap, options?: DifficultyCalculationOptions);
/**
* Returns a string representative of the class.
*/
toString(): string;
}
/**
* Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
*/
declare class OsuAim extends OsuSkill {
protected readonly skillMultiplier: number;
protected readonly strainDecayBase: number;
protected readonly reducedSectionCount: number;
protected readonly reducedSectionBaseline: number;
protected readonly decayWeight: number;
private readonly withSliders;
constructor(mods: Mod[], withSliders: boolean);
/**
* @param current The hitobject to calculate.
*/
protected strainValueAt(current: DifficultyHitObject): number;
/**
* @param current The hitobject to save to.
*/
protected saveToHitObject(current: DifficultyHitObject): void;
}
/**
* An evaluator for calculating osu!standard Aim skill.
*/
declare abstract class OsuAimEvaluator extends AimEvaluator {
/**
* Evaluates the difficulty of aiming the current object, based on:
*
* - cursor velocity to the current object,
* - angle difficulty,
* - sharp velocity increases,
* - and slider difficulty.
*
* @param current The current object.
* @param withSliders Whether to take slider difficulty into account.
*/
static evaluateDifficultyOf(current: DifficultyHitObject, withSliders: boolean): number;
}
/**
* Represents the skill required to memorize and hit every object in a beatmap with the Flashlight mod enabled.
*/
declare class OsuFlashlight extends OsuSkill {
protected readonly skillMultiplier: number;
protected readonly strainDecayBase: number;
protected readonly reducedSectionCount: number;
protected readonly reducedSectionBaseline: number;
protected readonly decayWeight: number;
private readonly isHidden;
constructor(mods: Mod[]);
/**
* @param current The hitobject to calculate.
*/
protected strainValueAt(current: DifficultyHitObject): number;
protected saveToHitObject(current: DifficultyHitObject): void;
}
/**
* An evaluator for calculating osu!standard Flashlight skill.
*/
declare abstract class OsuFlashlightEvaluator extends FlashlightEvaluator {
/**
* Evaluates the difficulty of memorizing and hitting the current object, based on:
*
* - distance between a number of previous objects and the current object,
* - the visual opacity of the current object,
* - the angle made by the current object,
* - length and speed of the current object (for sliders),
* - and whether Hidden mod is enabled.
*
* @param current The current object.
* @param isHiddenMod Whether the Hidden mod is enabled.
*/
static evaluateDifficultyOf(current: DifficultyHitObject, isHiddenMod: boolean): number;
}
/**
* A performance points calculator that calculates performance points for osu!standard gamemode.
*/
declare class OsuPerformanceCalculator extends PerformanceCalculator {
/**
* The aim performance value.
*/
aim: number;
/**
* The speed performance value.
*/
speed: number;
/**
* The accuracy performance value.
*/
accuracy: number;
/**
* The flashlight performance value.
*/
flashlight: number;
readonly difficultyAttributes: OsuDifficultyAttributes;
protected finalMultiplier: number;
protected readonly mode: Modes;
protected calculateValues(): void;
protected calculateTotalValue(): void;
/**
* @param difficultyAttributes The difficulty attributes to calculate.
*/
constructor(difficultyAttributes: OsuDifficultyAttributes);
/**
* Calculates the aim performance value of the beatmap.
*/
private calculateAimValue;
/**
* Calculates the speed performance value of the beatmap.
*/
private calculateSpeedValue;
/**
* Calculates the accuracy performance value of the beatmap.
*/
private calculateAccuracyValue;
/**
* Calculates the flashlight performance value of the beatmap.
*/
private calculateFlashlightValue;
toString(): string;
}
/**
* An evaluator for calculating osu!standard Rhythm skill.
*/
declare abstract class OsuRhythmEvaluator extends RhythmEvaluator {
/**
* Calculates a rhythm multiplier for the difficulty of the tap associated
* with historic data of the current object.
*
* @param current The current object.
* @param greatWindow The great hit window of the current object.
*/
static evaluateDifficultyOf(current: DifficultyHitObject, greatWindow: number): number;
}
/**
* Represents the skill required to press keys or tap with regards to keeping up with the speed at which objects need to be hit.
*/
declare class OsuSpeed extends OsuSkill {
protected readonly skillMultiplier: number;
protected readonly strainDecayBase: number;
protected readonly reducedSectionCount: number;
protected readonly reducedSectionBaseline: number;
protected readonly difficultyMultiplier: number;
protected readonly decayWeight: number;
private currentSpeedStrain;
private currentRhythm;
private readonly minSpeedBonus;
private readonly greatWindow;
constructor(mods: Mod[], greatWindow: number);
/**
* @param current The hitobject to calculate.
*/
protected strainValueAt(current: DifficultyHitObject): number;
/**
* @param current The hitobject to save to.
*/
protected saveToHitObject(current: DifficultyHitObject): void;
}
/**
* An evaluator for calculating osu!standard speed skill.
*/
de