@fukutotojido/osu-classes
Version:
Basic classes, interfaces and utils for creating new osu! rulesets
1,957 lines (1,718 loc) • 160 kB
JavaScript
class BeatmapColorSection {
comboColors = [];
sliderTrackColor;
sliderBorderColor;
clone() {
const cloned = new BeatmapColorSection();
cloned.comboColors = this.comboColors.map((c) => c.clone());
if (this.sliderTrackColor) {
cloned.sliderTrackColor = this.sliderTrackColor;
}
if (this.sliderBorderColor) {
cloned.sliderBorderColor = this.sliderBorderColor;
}
return cloned;
}
}
class BeatmapDifficultySection {
static BASE_DIFFICULTY = 5;
_CS = BeatmapDifficultySection.BASE_DIFFICULTY;
_HP = BeatmapDifficultySection.BASE_DIFFICULTY;
_OD = BeatmapDifficultySection.BASE_DIFFICULTY;
_AR;
_multiplier = 1;
_tickRate = 1;
_rate = 1;
get circleSize() {
return Math.fround(this._CS);
}
set circleSize(value) {
this._CS = value;
}
get drainRate() {
return Math.fround(this._HP);
}
set drainRate(value) {
this._HP = value;
}
get overallDifficulty() {
return Math.fround(this._OD);
}
set overallDifficulty(value) {
this._OD = value;
}
get approachRate() {
return Math.fround(this._AR ?? this._OD);
}
set approachRate(value) {
this._AR = value;
}
get sliderMultiplier() {
return this._multiplier;
}
set sliderMultiplier(value) {
this._multiplier = value;
}
get sliderTickRate() {
return this._tickRate;
}
set sliderTickRate(value) {
this._tickRate = value;
}
get clockRate() {
return this._rate;
}
set clockRate(value) {
this._rate = value;
}
clone() {
const cloned = new BeatmapDifficultySection();
cloned.circleSize = this._CS;
cloned.drainRate = this._HP;
cloned.overallDifficulty = this._OD;
if (typeof this._AR === 'number') {
cloned.approachRate = this._AR;
}
cloned.sliderMultiplier = this._multiplier;
cloned.sliderTickRate = this._tickRate;
cloned.clockRate = this._rate;
return cloned;
}
static range(diff, min, mid, max) {
if (diff > 5) {
return mid + ((max - mid) * (diff - 5)) / 5;
}
if (diff < 5) {
return mid - ((mid - min) * (5 - diff)) / 5;
}
return mid;
}
}
class BeatmapEditorSection {
bookmarks = [];
distanceSpacing = 1;
beatDivisor = 4;
gridSize = 1;
timelineZoom = 2;
clone() {
const cloned = new BeatmapEditorSection();
cloned.bookmarks = this.bookmarks.slice();
cloned.distanceSpacing = this.distanceSpacing;
cloned.beatDivisor = this.beatDivisor;
cloned.gridSize = this.gridSize;
cloned.timelineZoom = this.timelineZoom;
return cloned;
}
}
let BlendingMode;
(function(BlendingMode) {
BlendingMode[BlendingMode['AdditiveBlending'] = 0] = 'AdditiveBlending';
BlendingMode[BlendingMode['AlphaBlending'] = 1] = 'AlphaBlending';
})(BlendingMode || (BlendingMode = {}));
class BlendingParameters {
source;
destination;
sourceAlpha;
destinationAlpha;
rgbEquation;
alphaEquation;
static None = new BlendingParameters({
source: 5,
destination: 15,
sourceAlpha: 5,
destinationAlpha: 15,
rgbEquation: 1,
alphaEquation: 1,
});
static Inherit = new BlendingParameters({
source: 0,
destination: 0,
sourceAlpha: 0,
destinationAlpha: 0,
rgbEquation: 0,
alphaEquation: 0,
});
static Mixture = new BlendingParameters({
source: 12,
destination: 10,
sourceAlpha: 5,
destinationAlpha: 5,
rgbEquation: 1,
alphaEquation: 1,
});
static Additive = new BlendingParameters({
source: 12,
destination: 5,
sourceAlpha: 5,
destinationAlpha: 5,
rgbEquation: 1,
alphaEquation: 1,
});
constructor(params) {
this.source = params.source || 0;
this.destination = params.destination || 0;
this.sourceAlpha = params.sourceAlpha || 0;
this.destinationAlpha = params.destinationAlpha || 0;
this.rgbEquation = params.rgbEquation || 0;
this.alphaEquation = params.alphaEquation || 0;
}
copyFromParent(parent) {
if (this.source === 0) {
this.source = parent.source;
}
if (this.destination === 0) {
this.destination = parent.destination;
}
if (this.sourceAlpha === 0) {
this.sourceAlpha = parent.sourceAlpha;
}
if (this.destinationAlpha === 0) {
this.destinationAlpha = parent.destinationAlpha;
}
if (this.rgbEquation === 0) {
this.rgbEquation = parent.rgbEquation;
}
if (this.alphaEquation === 0) {
this.alphaEquation = parent.alphaEquation;
}
}
applyDefaultToInherited() {
if (this.source === 0) {
this.source = 12;
}
if (this.destination === 0) {
this.destination = 10;
}
if (this.sourceAlpha === 0) {
this.sourceAlpha = 5;
}
if (this.destinationAlpha === 0) {
this.destinationAlpha = 5;
}
if (this.rgbEquation === 0) {
this.rgbEquation = 1;
}
if (this.alphaEquation === 0) {
this.alphaEquation = 1;
}
}
equals(other) {
return other.source === this.source
&& other.destination === this.destination
&& other.sourceAlpha === this.sourceAlpha
&& other.destinationAlpha === this.destinationAlpha
&& other.rgbEquation === this.rgbEquation
&& other.alphaEquation === this.alphaEquation;
}
get isDisabled() {
return this.source === 5
&& this.destination === 15
&& this.sourceAlpha === 5
&& this.destinationAlpha === 15
&& this.rgbEquation === 1
&& this.alphaEquation === 1;
}
get rgbEquationMode() {
return BlendingParameters._translateEquation(this.rgbEquation);
}
get alphaEquationMode() {
return BlendingParameters._translateEquation(this.alphaEquation);
}
get sourceBlendingFactor() {
return BlendingParameters._translateBlendingFactorSrc(this.source);
}
get destinationBlendingFactor() {
return BlendingParameters._translateBlendingFactorDest(this.destination);
}
get sourceAlphaBlendingFactor() {
return BlendingParameters._translateBlendingFactorSrc(this.sourceAlpha);
}
get destinationAlphaBlendingFactor() {
return BlendingParameters._translateBlendingFactorDest(this.destinationAlpha);
}
static _translateBlendingFactorSrc(factor) {
switch (factor) {
case 1:
return 32771;
case 2:
return 32769;
case 3:
return 772;
case 4:
return 774;
case 5:
return 1;
case 6:
return 32772;
case 7:
return 32770;
case 8:
return 773;
case 9:
return 775;
case 10:
return 769;
case 12:
return 770;
case 13:
return 776;
case 14:
return 768;
default:
case 15:
return 0;
}
}
static _translateBlendingFactorDest(factor) {
switch (factor) {
case 1:
return 32771;
case 2:
return 32769;
case 3:
return 772;
case 4:
return 774;
case 5:
return 1;
case 6:
return 32772;
case 7:
return 32770;
case 8:
return 773;
case 9:
return 775;
case 10:
return 771;
case 11:
return 769;
case 12:
return 770;
case 13:
return 776;
case 14:
return 768;
default:
case 15:
return 0;
}
}
static _translateEquation(equation) {
switch (equation) {
default:
case 0:
case 1:
return 32774;
case 2:
return 32775;
case 3:
return 32776;
case 4:
return 32778;
case 5:
return 32779;
}
}
}
let Anchor;
(function(Anchor) {
Anchor[Anchor['y0'] = 1] = 'y0';
Anchor[Anchor['y1'] = 2] = 'y1';
Anchor[Anchor['y2'] = 4] = 'y2';
Anchor[Anchor['x0'] = 8] = 'x0';
Anchor[Anchor['x1'] = 16] = 'x1';
Anchor[Anchor['x2'] = 32] = 'x2';
Anchor[Anchor['Custom'] = 64] = 'Custom';
Anchor[Anchor['TopLeft'] = 9] = 'TopLeft';
Anchor[Anchor['TopCentre'] = 17] = 'TopCentre';
Anchor[Anchor['TopRight'] = 33] = 'TopRight';
Anchor[Anchor['CentreLeft'] = 10] = 'CentreLeft';
Anchor[Anchor['Centre'] = 18] = 'Centre';
Anchor[Anchor['CentreRight'] = 34] = 'CentreRight';
Anchor[Anchor['BottomLeft'] = 12] = 'BottomLeft';
Anchor[Anchor['BottomCentre'] = 20] = 'BottomCentre';
Anchor[Anchor['BottomRight'] = 36] = 'BottomRight';
})(Anchor || (Anchor = {}));
let CommandType;
(function(CommandType) {
CommandType['None'] = '';
CommandType['Movement'] = 'M';
CommandType['MovementX'] = 'MX';
CommandType['MovementY'] = 'MY';
CommandType['Fade'] = 'F';
CommandType['Scale'] = 'S';
CommandType['VectorScale'] = 'V';
CommandType['Rotation'] = 'R';
CommandType['Color'] = 'C';
CommandType['Parameter'] = 'P';
})(CommandType || (CommandType = {}));
let CompoundType;
(function(CompoundType) {
CompoundType['None'] = '';
CompoundType['Loop'] = 'L';
CompoundType['Trigger'] = 'T';
})(CompoundType || (CompoundType = {}));
let EventType;
(function(EventType) {
EventType[EventType['Background'] = 0] = 'Background';
EventType[EventType['Video'] = 1] = 'Video';
EventType[EventType['Break'] = 2] = 'Break';
EventType[EventType['Colour'] = 3] = 'Colour';
EventType[EventType['Sprite'] = 4] = 'Sprite';
EventType[EventType['Sample'] = 5] = 'Sample';
EventType[EventType['Animation'] = 6] = 'Animation';
EventType[EventType['StoryboardCommand'] = 7] = 'StoryboardCommand';
})(EventType || (EventType = {}));
let LayerType;
(function(LayerType) {
LayerType[LayerType['Background'] = 0] = 'Background';
LayerType[LayerType['Fail'] = 1] = 'Fail';
LayerType[LayerType['Pass'] = 2] = 'Pass';
LayerType[LayerType['Foreground'] = 3] = 'Foreground';
LayerType[LayerType['Overlay'] = 4] = 'Overlay';
LayerType[LayerType['Video'] = 5] = 'Video';
})(LayerType || (LayerType = {}));
let LoopType;
(function(LoopType) {
LoopType[LoopType['LoopForever'] = 0] = 'LoopForever';
LoopType[LoopType['LoopOnce'] = 1] = 'LoopOnce';
})(LoopType || (LoopType = {}));
let Origins;
(function(Origins) {
Origins[Origins['TopLeft'] = 0] = 'TopLeft';
Origins[Origins['Centre'] = 1] = 'Centre';
Origins[Origins['CentreLeft'] = 2] = 'CentreLeft';
Origins[Origins['TopRight'] = 3] = 'TopRight';
Origins[Origins['BottomCentre'] = 4] = 'BottomCentre';
Origins[Origins['TopCentre'] = 5] = 'TopCentre';
Origins[Origins['Custom'] = 6] = 'Custom';
Origins[Origins['CentreRight'] = 7] = 'CentreRight';
Origins[Origins['BottomLeft'] = 8] = 'BottomLeft';
Origins[Origins['BottomRight'] = 9] = 'BottomRight';
})(Origins || (Origins = {}));
let ParameterType;
(function(ParameterType) {
ParameterType['None'] = '';
ParameterType['HorizontalFlip'] = 'H';
ParameterType['VerticalFlip'] = 'V';
ParameterType['BlendingMode'] = 'A';
})(ParameterType || (ParameterType = {}));
class Color4 {
red;
green;
blue;
alpha;
constructor(red, green, blue, alpha) {
this.red = red ?? 255;
this.green = green ?? 255;
this.blue = blue ?? 255;
this.alpha = alpha ?? 1;
}
get hex() {
const alpha = Math.round(this.alpha * 255);
return ('#' +
this.red.toString(16).padStart(2, '0') +
this.green.toString(16).padStart(2, '0') +
this.blue.toString(16).padStart(2, '0') +
alpha.toString(16).padStart(2, '0'));
}
equals(color) {
return this.red === color.red
&& this.green === color.green
&& this.blue === color.blue
&& this.alpha === color.alpha;
}
clone() {
return new Color4(this.red, this.green, this.blue, this.alpha);
}
toString() {
return `${this.red},${this.green},${this.blue},${this.alpha}`;
}
}
class Vector2 {
x;
y;
constructor(x, y) {
this.x = x;
this.y = isFinite(y) ? y : x;
}
get floatX() {
return Math.fround(this.x);
}
get floatY() {
return Math.fround(this.y);
}
add(vec) {
return new Vector2(this.x + vec.x, this.y + vec.y);
}
fadd(vec) {
return new Vector2(Math.fround(this.floatX + vec.floatX), Math.fround(this.floatY + vec.floatY));
}
subtract(vec) {
return new Vector2(this.x - vec.x, this.y - vec.y);
}
fsubtract(vec) {
return new Vector2(Math.fround(this.floatX - vec.floatX), Math.fround(this.floatY - vec.floatY));
}
scale(multiplier) {
return new Vector2(this.x * multiplier, this.y * multiplier);
}
fscale(multiplier) {
const floatMultiplier = Math.fround(multiplier);
return new Vector2(Math.fround(this.floatX * floatMultiplier), Math.fround(this.floatY * floatMultiplier));
}
divide(divisor) {
return new Vector2(this.x / divisor, this.y / divisor);
}
fdivide(divisor) {
const floatDivisor = Math.fround(divisor);
return new Vector2(Math.fround(this.floatX / floatDivisor), Math.fround(this.floatY / floatDivisor));
}
dot(vec) {
return this.x * vec.x + this.y * vec.y;
}
fdot(vec) {
return Math.fround(Math.fround(Math.fround(this.floatX * vec.floatX) +
Math.fround(this.floatY * vec.floatY)));
}
length() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
flength() {
return Math.fround(Math.sqrt(Math.fround(Math.fround(this.floatX * this.floatX) +
Math.fround(this.floatY * this.floatY))));
}
distance(vec) {
const x = this.x - vec.x;
const y = this.y - vec.y;
return Math.sqrt(x * x + y * y);
}
fdistance(vec) {
const x = Math.fround(this.floatX - vec.floatX);
const y = Math.fround(this.floatY - vec.floatY);
return Math.fround(Math.sqrt(Math.fround(Math.fround(x * x) + Math.fround(y * y))));
}
normalize() {
const scale = 1 / this.length();
return new Vector2(this.x * scale, this.y * scale);
}
fnormalize() {
const scale = Math.fround(1 / this.flength());
return new Vector2(Math.fround(this.floatX * scale), Math.fround(this.floatY * scale));
}
equals(vec) {
return this.x === vec.x && this.y === vec.y;
}
clone() {
return new Vector2(this.x, this.y);
}
toString() {
return `${this.x},${this.y}`;
}
}
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function clamp01(value) {
return clamp(value, 0, 1);
}
function map(value, from1, to1, from2, to2) {
if (value < from1) {
return from2;
}
if (value >= to1) {
return to2;
}
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
}
function map01(value, from1, to1) {
return map(value, from1, to1, 0, 1);
}
function lerp(value, a, b) {
return a * (1 - value) + b * value;
}
function lerpClamped01(value, a, b) {
return lerp(clamp01(value), a, b);
}
function lerpVector2(value, a, b) {
const x = lerpClamped01(value, a.floatX, b.floatX);
const y = lerpClamped01(value, a.floatY, b.floatY);
return new Vector2(x, y);
}
function lerpColor4(value, a, b) {
const red = lerpClamped01(value, a.red, b.red);
const green = lerpClamped01(value, a.green, b.green);
const blue = lerpClamped01(value, a.blue, b.blue);
const alpha = lerpClamped01(value, a.alpha, b.alpha);
return new Color4(red, green, blue, alpha);
}
let MathUtils = /* #__PURE__*/Object.freeze({
__proto__: null,
clamp,
clamp01,
lerp,
lerpClamped01,
lerpColor4,
lerpVector2,
map,
map01,
});
let EasingType;
(function(EasingType) {
EasingType[EasingType['None'] = 0] = 'None';
EasingType[EasingType['Out'] = 1] = 'Out';
EasingType[EasingType['In'] = 2] = 'In';
EasingType[EasingType['InQuad'] = 3] = 'InQuad';
EasingType[EasingType['OutQuad'] = 4] = 'OutQuad';
EasingType[EasingType['InOutQuad'] = 5] = 'InOutQuad';
EasingType[EasingType['InCubic'] = 6] = 'InCubic';
EasingType[EasingType['OutCubic'] = 7] = 'OutCubic';
EasingType[EasingType['InOutCubic'] = 8] = 'InOutCubic';
EasingType[EasingType['InQuart'] = 9] = 'InQuart';
EasingType[EasingType['OutQuart'] = 10] = 'OutQuart';
EasingType[EasingType['InOutQuart'] = 11] = 'InOutQuart';
EasingType[EasingType['InQuint'] = 12] = 'InQuint';
EasingType[EasingType['OutQuint'] = 13] = 'OutQuint';
EasingType[EasingType['InOutQuint'] = 14] = 'InOutQuint';
EasingType[EasingType['InSine'] = 15] = 'InSine';
EasingType[EasingType['OutSine'] = 16] = 'OutSine';
EasingType[EasingType['InOutSine'] = 17] = 'InOutSine';
EasingType[EasingType['InExpo'] = 18] = 'InExpo';
EasingType[EasingType['OutExpo'] = 19] = 'OutExpo';
EasingType[EasingType['InOutExpo'] = 20] = 'InOutExpo';
EasingType[EasingType['InCirc'] = 21] = 'InCirc';
EasingType[EasingType['OutCirc'] = 22] = 'OutCirc';
EasingType[EasingType['InOutCirc'] = 23] = 'InOutCirc';
EasingType[EasingType['InElastic'] = 24] = 'InElastic';
EasingType[EasingType['OutElastic'] = 25] = 'OutElastic';
EasingType[EasingType['OutElasticHalf'] = 26] = 'OutElasticHalf';
EasingType[EasingType['OutElasticQuarter'] = 27] = 'OutElasticQuarter';
EasingType[EasingType['InOutElastic'] = 28] = 'InOutElastic';
EasingType[EasingType['InBack'] = 29] = 'InBack';
EasingType[EasingType['OutBack'] = 30] = 'OutBack';
EasingType[EasingType['InOutBack'] = 31] = 'InOutBack';
EasingType[EasingType['InBounce'] = 32] = 'InBounce';
EasingType[EasingType['OutBounce'] = 33] = 'OutBounce';
EasingType[EasingType['InOutBounce'] = 34] = 'InOutBounce';
EasingType[EasingType['OutPow10'] = 35] = 'OutPow10';
})(EasingType || (EasingType = {}));
const ELASTIC_CONST = 2 * Math.PI / 0.3;
const ELASTIC_CONST2 = 0.3 / 4;
const BACK_CONST = 1.70158;
const BACK_CONST2 = BACK_CONST * 1.525;
const BOUNCE_CONST = 1 / 2.75;
const EXPO_OFFSET = Math.pow(2, -10);
const ELASTIC_OFFSET_FULL = Math.pow(2, -11);
const ELASTIC_OFFSET_HALF = Math.pow(2, -10) * Math.sin((0.5 - ELASTIC_CONST2) * ELASTIC_CONST);
const ELASTIC_OFFSET_QUARTER = Math.pow(2, -10) * Math.sin((0.25 - ELASTIC_CONST2) * ELASTIC_CONST);
const IN_OUT_ELASTIC_OFFSET = Math.pow(2, -10) * Math.sin(((1 - ELASTIC_CONST2 * 1.5) * ELASTIC_CONST) / 1.5);
const clampEase = (fn) => (p) => fn(clamp01(p));
const linear = clampEase((p) => p);
const inQuad = clampEase((p) => p * p);
const outQuad = clampEase((p) => p * (2 - p));
const inOutQuad = clampEase((p) => {
if (p < 0.5) {
return p * p * 2;
}
return --p * p * -2 + 1;
});
const inCubic = clampEase((p) => p * p * p);
const outCubic = clampEase((p) => --p * p * p + 1);
const inOutCubic = clampEase((p) => {
if (p < 0.5) {
return p * p * p * 4;
}
return --p * p * p * 4 + 1;
});
const inQuart = clampEase((p) => p * p * p * p);
const outQuart = clampEase((p) => 1 - --p * p * p * p);
const inOutQuart = clampEase((p) => {
if (p < 0.5) {
return p * p * p * p * 8;
}
return --p * p * p * p * -8 + 1;
});
const inQuint = clampEase((p) => {
return p * p * p * p * p;
});
const outQuint = clampEase((p) => {
return --p * p * p * p * p + 1;
});
const inOutQuint = clampEase((p) => {
if (p < 0.5) {
return p * p * p * p * p * 16;
}
return --p * p * p * p * p * 16 + 1;
});
const inSine = clampEase((p) => {
return 1 - Math.cos(p * Math.PI * 0.5);
});
const outSine = clampEase((p) => {
return Math.sin(p * Math.PI * 0.5);
});
const inOutSine = clampEase((p) => {
return 0.5 - 0.5 * Math.cos(Math.PI * p);
});
const inExpo = clampEase((p) => {
return Math.pow(2, 10 * (p - 1)) + EXPO_OFFSET * (p - 1);
});
const outExpo = clampEase((p) => {
return -Math.pow(2, -10 * p) + 1 + EXPO_OFFSET * p;
});
const inOutExpo = clampEase((p) => {
if (p < 0.5) {
return 0.5 * (Math.pow(2, 20 * p - 10) + EXPO_OFFSET * (2 * p - 1));
}
return 1 - 0.5 * (Math.pow(2, -20 * p + 10) + EXPO_OFFSET * (-2 * p + 1));
});
const inCirc = clampEase((p) => {
return 1 - Math.sqrt(1 - p * p);
});
const outCirc = clampEase((p) => {
return Math.sqrt(1 - --p * p);
});
const inOutCirc = clampEase((p) => {
if ((p *= 2) < 1) {
return 0.5 - 0.5 * Math.sqrt(1 - p * p);
}
return 0.5 * Math.sqrt(1 - (p -= 2) * p) + 0.5;
});
const inElastic = clampEase((p) => {
return -Math.pow(2, -10 + 10 * p) * Math.sin((1 - ELASTIC_CONST2 - p) * ELASTIC_CONST) + ELASTIC_OFFSET_FULL * (1 - p);
});
const outElastic = clampEase((p) => {
return Math.pow(2, -10 * p) * Math.sin((p - ELASTIC_CONST2) * ELASTIC_CONST) + 1 - ELASTIC_OFFSET_FULL * p;
});
const outElasticHalf = clampEase((p) => {
return Math.pow(2, -10 * p) * Math.sin((0.5 * p - ELASTIC_CONST2) * ELASTIC_CONST) + 1 - ELASTIC_OFFSET_HALF * p;
});
const outElasticQuarter = clampEase((p) => {
return Math.pow(2, -10 * p) * Math.sin((0.25 * p - ELASTIC_CONST2) * ELASTIC_CONST) + 1 - ELASTIC_OFFSET_QUARTER * p;
});
const inOutElastic = clampEase((p) => {
if ((p *= 2) < 1) {
return -0.5 * (Math.pow(2, -10 + 10 * p) * Math.sin(((1 - ELASTIC_CONST2 * 1.5 - p) * ELASTIC_CONST) / 1.5) - IN_OUT_ELASTIC_OFFSET * (1 - p));
}
return 0.5 * (Math.pow(2, -10 * --p) * Math.sin(((p - ELASTIC_CONST2 * 1.5) * ELASTIC_CONST) / 1.5) - IN_OUT_ELASTIC_OFFSET * p) + 1;
});
const inBack = clampEase((p) => {
return p * p * ((BACK_CONST + 1) * p - BACK_CONST);
});
const outBack = clampEase((p) => {
return --p * p * ((BACK_CONST + 1) * p + BACK_CONST) + 1;
});
const inOutBack = clampEase((p) => {
if ((p *= 2) < 1) {
return 0.5 * p * p * ((BACK_CONST2 + 1) * p - BACK_CONST2);
}
return 0.5 * ((p -= 2) * p * ((BACK_CONST2 + 1) * p + BACK_CONST2) + 2);
});
const inBounce = clampEase((p) => {
p = 1 - p;
if (p < BOUNCE_CONST) {
return 1 - 7.5625 * p * p;
}
if (p < 2 * BOUNCE_CONST) {
return 1 - (7.5625 * (p -= 1.5 * BOUNCE_CONST) * p + 0.75);
}
if (p < 2.5 * BOUNCE_CONST) {
return 1 - (7.5625 * (p -= 2.25 * BOUNCE_CONST) * p + 0.9375);
}
return 1 - (7.5625 * (p -= 2.625 * BOUNCE_CONST) * p + 0.984375);
});
const outBounce = clampEase((p) => {
if (p < BOUNCE_CONST) {
return 7.5625 * p * p;
}
if (p < 2 * BOUNCE_CONST) {
return 7.5625 * (p -= 1.5 * BOUNCE_CONST) * p + 0.75;
}
if (p < 2.5 * BOUNCE_CONST) {
return 7.5625 * (p -= 2.25 * BOUNCE_CONST) * p + 0.9375;
}
return 7.5625 * (p -= 2.625 * BOUNCE_CONST) * p + 0.984375;
});
const inOutBounce = clampEase((p) => {
if (p < 0.5) {
return 0.5 - 0.5 * outBounce(1 - p * 2);
}
return outBounce((p - 0.5) * 2) * 0.5 + 0.5;
});
const outPow10 = clampEase((p) => {
return --p * Math.pow(p, 10) + 1;
});
function getEasingFn(easing) {
switch (easing) {
case EasingType.In:
case EasingType.InQuad: return inQuad;
case EasingType.Out:
case EasingType.OutQuad: return outQuad;
case EasingType.InOutQuad: return inOutQuad;
case EasingType.InCubic: return inCubic;
case EasingType.OutCubic: return outCubic;
case EasingType.InOutCubic: return inOutCubic;
case EasingType.InQuart: return inQuart;
case EasingType.OutQuart: return outQuart;
case EasingType.InOutQuart: return inOutQuart;
case EasingType.InQuint: return inQuint;
case EasingType.OutQuint: return outQuint;
case EasingType.InOutQuint: return inOutQuint;
case EasingType.InSine: return inSine;
case EasingType.OutSine: return outSine;
case EasingType.InOutSine: return inOutSine;
case EasingType.InExpo: return inExpo;
case EasingType.OutExpo: return outExpo;
case EasingType.InOutExpo: return inOutExpo;
case EasingType.InCirc: return inCirc;
case EasingType.OutCirc: return outCirc;
case EasingType.InOutCirc: return inOutCirc;
case EasingType.InElastic: return inElastic;
case EasingType.OutElastic: return outElastic;
case EasingType.OutElasticHalf: return outElasticHalf;
case EasingType.OutElasticQuarter: return outElasticQuarter;
case EasingType.InOutElastic: return inOutElastic;
case EasingType.InBack: return inBack;
case EasingType.OutBack: return outBack;
case EasingType.InOutBack: return inOutBack;
case EasingType.InBounce: return inBounce;
case EasingType.OutBounce: return outBounce;
case EasingType.InOutBounce: return inOutBounce;
case EasingType.OutPow10: return outPow10;
}
return linear;
}
let Easing = /* #__PURE__*/Object.freeze({
__proto__: null,
getEasingFn,
inBack,
inBounce,
inCirc,
inCubic,
inElastic,
inExpo,
inOutBack,
inOutBounce,
inOutCirc,
inOutCubic,
inOutElastic,
inOutExpo,
inOutQuad,
inOutQuart,
inOutQuint,
inOutSine,
inQuad,
inQuart,
inQuint,
inSine,
linear,
outBack,
outBounce,
outCirc,
outCubic,
outElastic,
outElasticHalf,
outElasticQuarter,
outExpo,
outPow10,
outQuad,
outQuart,
outQuint,
outSine,
});
class Command {
type;
parameter;
easing;
startTime;
endTime;
constructor(params) {
this.type = params?.type ?? CommandType.None;
this.parameter = params?.parameter ?? ParameterType.None;
this.easing = params?.easing ?? EasingType.None;
this.startTime = params?.startTime ?? 0;
this.endTime = params?.endTime ?? 0;
this.startValue = params?.startValue ?? null;
this.endValue = params?.endValue ?? null;
}
get duration() {
return this.endTime - this.startTime;
}
getProgress(time) {
const clamped = map01(time, this.startTime, this.endTime);
return getEasingFn(this.easing)(clamped);
}
getValueAtProgress(progress) {
const getNumber = (progress, start, end) => {
return start + progress * (end - start);
};
const getBoolean = (time, start, end) => {
return start === end || time >= start && time < end;
};
if (typeof this.startValue === 'number') {
const startValue = this.startValue;
const endValue = this.endValue;
return getNumber(progress, startValue, endValue);
}
if (this.startValue instanceof Vector2) {
const startValue = this.startValue;
const endValue = this.endValue;
return new Vector2(getNumber(progress, startValue.floatX, endValue.floatX), getNumber(progress, startValue.floatY, endValue.floatY));
}
if (this.startValue instanceof Color4) {
const startValue = this.startValue;
const endValue = this.endValue;
return new Color4(getNumber(progress, startValue.red, endValue.red), getNumber(progress, startValue.green, endValue.green), getNumber(progress, startValue.blue, endValue.blue), getNumber(progress, startValue.alpha, endValue.alpha));
}
const time = this.startTime + this.duration * progress;
if (typeof this.startValue === 'boolean') {
return getBoolean(time, this.startTime, this.endTime);
}
if (this.startValue instanceof BlendingParameters) {
const startValue = this.startValue;
const endValue = this.endValue;
const isAdditive = getBoolean(time, this.startTime, this.endTime);
return isAdditive ? startValue : endValue;
}
return this.endValue;
}
getValueAtTime(time) {
return this.getValueAtProgress(this.getProgress(time));
}
equals(other) {
return this.type === other.type
&& this.startTime === other.startTime
&& this.endTime === other.endTime
&& this.startValue === other.startValue
&& this.endValue === other.endValue
&& this.easing === other.easing
&& this.parameter === other.parameter;
}
}
class CommandTimeline {
_commands = [];
startTime = Infinity;
endTime = -Infinity;
[Symbol.iterator]() {
const data = this.commands;
let i = -1;
return {
next: () => ({ value: data[++i], done: !(i in data) }),
};
}
get commands() {
return this._commands.sort((a, b) => {
return a.startTime - b.startTime || a.endTime - b.endTime;
});
}
add(type, easing, startTime, endTime, startValue, endValue, parameter) {
if (endTime < startTime) {
[startTime, endTime] = [endTime, startTime];
[startValue, endValue] = [endValue, startValue];
}
this._commands.push(new Command({
type,
easing,
startTime,
endTime,
startValue,
endValue,
parameter,
}));
if (startTime < this.startTime) {
this.startValue = startValue;
this.startTime = startTime;
}
if (endTime > this.endTime) {
this.endValue = endValue;
this.endTime = endTime;
}
}
get hasCommands() {
return this._commands.length > 0;
}
}
class CommandTimelineGroup {
x = new CommandTimeline();
y = new CommandTimeline();
scale = new CommandTimeline();
vectorScale = new CommandTimeline();
rotation = new CommandTimeline();
color = new CommandTimeline();
alpha = new CommandTimeline();
blendingParameters = new CommandTimeline();
flipH = new CommandTimeline();
flipV = new CommandTimeline();
_timelines;
constructor() {
this._timelines = [
this.x,
this.y,
this.scale,
this.vectorScale,
this.rotation,
this.color,
this.alpha,
this.blendingParameters,
this.flipH,
this.flipV,
];
}
get timelines() {
return this._timelines;
}
get totalCommands() {
return this._timelines.reduce((c, t) => c + t.commands.length, 0);
}
get commands() {
return this._timelines
.flatMap((t) => t.commands)
.sort((a, b) => a.startTime - b.startTime || a.endTime - b.endTime);
}
get commandsStartTime() {
let min = Infinity;
for (let i = 0; i < this._timelines.length; ++i) {
min = Math.min(min, this._timelines[i].startTime);
}
return min;
}
get commandsEndTime() {
let max = -Infinity;
for (let i = 0; i < this._timelines.length; ++i) {
max = Math.max(max, this._timelines[i].endTime);
}
return max;
}
get commandsDuration() {
return this.commandsEndTime - this.commandsStartTime;
}
get startTime() {
return this.commandsStartTime;
}
get endTime() {
return this.commandsEndTime;
}
get duration() {
return this.endTime - this.startTime;
}
get hasCommands() {
for (let i = 0; i < this._timelines.length; ++i) {
if (this._timelines[i].hasCommands) {
return true;
}
}
return false;
}
}
class CommandLoop extends CommandTimelineGroup {
type = CompoundType.Loop;
loopStartTime;
loopCount;
constructor(loopStartTime, loopCount) {
super();
this.loopStartTime = loopStartTime || 0;
this.loopCount = loopCount || 0;
}
get totalIterations() {
return this.loopCount + 1;
}
get startTime() {
return this.loopStartTime + this.commandsStartTime;
}
get endTime() {
return this.startTime + this.commandsDuration * this.totalIterations;
}
unrollCommands() {
const commands = this.commands;
if (commands.length === 0) {
return [];
}
const { commandsDuration, totalIterations, loopStartTime } = this;
const unrolledCommands = new Array(totalIterations * commands.length);
for (let i = 0; i < totalIterations; i++) {
const iterationStartTime = loopStartTime + i * commandsDuration;
for (let j = 0; j < commands.length; j++) {
const currentIndex = i * commands.length + j;
const command = commands[j];
unrolledCommands[currentIndex] = new Command({
...command,
startTime: command.startTime + iterationStartTime,
endTime: command.endTime + iterationStartTime,
});
}
}
return unrolledCommands;
}
}
class CommandTrigger extends CommandTimelineGroup {
type = CompoundType.Trigger;
triggerName;
triggerStartTime;
triggerEndTime;
groupNumber;
constructor(triggerName, startTime, endTime, groupNumber) {
super();
this.triggerName = triggerName || '';
this.triggerStartTime = startTime || 0;
this.triggerEndTime = endTime || 0;
this.groupNumber = groupNumber || 0;
}
unrollCommands() {
const commands = this.commands;
if (commands.length === 0) {
return [];
}
const unrolledCommands = new Array(commands.length);
for (let i = 0; i < commands.length; i++) {
unrolledCommands[i] = new Command({
...commands[i],
startTime: commands[i].startTime + this.triggerStartTime,
endTime: commands[i].endTime + this.triggerStartTime,
});
}
return unrolledCommands;
}
}
class StoryboardSprite {
origin;
anchor;
startTime = Infinity;
endTime = -Infinity;
filePath;
commands = [];
timelineGroup = new CommandTimelineGroup();
loops = [];
triggers = [];
startPosition;
scale = new Vector2(1, 1);
color = new Color4();
rotation = 0;
flipX = false;
flipY = false;
isAdditive = false;
constructor(path, origin, anchor, position) {
this.filePath = path ?? '';
this.origin = origin ?? Origins.TopLeft;
this.anchor = anchor ?? Anchor.TopLeft;
this.startPosition = position ?? new Vector2(0, 0);
}
get startX() {
return this.startPosition.floatX;
}
set startX(value) {
this.startPosition.x = value;
}
get startY() {
return this.startPosition.floatY;
}
set startY(value) {
this.startPosition.y = value;
}
get duration() {
return this.endTime - this.startTime;
}
get hasCommands() {
return this.timelineGroup.hasCommands
|| this.loops.some((l) => l.hasCommands)
|| this.triggers.some((t) => t.hasCommands);
}
get isDrawable() {
return this.color.alpha >= 0.01 && this.hasCommands;
}
addLoop(startTime, repeatCount) {
const loop = new CommandLoop(startTime, repeatCount);
this.loops.push(loop);
return loop;
}
addTrigger(triggerName, startTime, endTime, groupNumber) {
const trigger = new CommandTrigger(triggerName, startTime, endTime, groupNumber);
this.triggers.push(trigger);
return trigger;
}
updateCommands() {
const unwinded = [
...this.timelineGroup.commands,
...this.loops.flatMap((l) => l.unrollCommands()),
];
this.commands = unwinded.sort((a, b) => a.startTime - b.startTime);
return this.commands;
}
adjustTimesToCommands() {
let earliestStartTime = this.startTime;
let latestEndTime = this.endTime;
for (const command of this.commands) {
earliestStartTime = Math.min(earliestStartTime, command.startTime);
latestEndTime = Math.max(latestEndTime, command.endTime);
}
this.startTime = earliestStartTime;
this.endTime = latestEndTime;
}
resetValuesToCommands() {
const applied = {};
for (const command of this.commands) {
if (!applied[command.type]) {
this.setValueFromCommand(command, 0);
applied[command.type] = true;
}
}
}
setValueFromCommand(command, progress) {
const value = command.getValueAtProgress(progress);
switch (command.type) {
case CommandType.Movement:
this.startPosition.x = value.x;
this.startPosition.y = value.y;
break;
case CommandType.MovementX:
this.startPosition.x = value;
break;
case CommandType.MovementY:
this.startPosition.y = value;
break;
case CommandType.Fade:
this.color.alpha = value;
break;
case CommandType.Scale:
this.scale.x = value;
this.scale.y = value;
break;
case CommandType.VectorScale:
this.scale.x = value.x;
this.scale.y = value.y;
break;
case CommandType.Rotation:
this.rotation = value;
break;
case CommandType.Color:
this.color.red = value.red;
this.color.green = value.green;
this.color.blue = value.blue;
break;
}
if (command.type !== CommandType.Parameter) {
return;
}
switch (command.parameter) {
case ParameterType.BlendingMode:
this.isAdditive = value.rgbEquation === 1;
break;
case ParameterType.HorizontalFlip:
this.flipX = value;
break;
case ParameterType.VerticalFlip:
this.flipY = value;
}
}
}
class StoryboardAnimation extends StoryboardSprite {
frameCount;
frameDelay;
loopType;
constructor(path, origin, anchor, position, frameCount, frameDelay, loopType) {
super(path, origin, anchor, position);
this.frameCount = frameCount ?? 0;
this.frameDelay = frameDelay ?? 0;
this.loopType = loopType ?? LoopType.LoopForever;
}
}
class StoryboardSample {
startTime;
volume;
filePath;
get isDrawable() {
return true;
}
constructor(path, time, volume) {
this.filePath = path ?? '';
this.startTime = time ?? 0;
this.volume = volume ?? 100;
}
}
class StoryboardVideo {
startTime;
filePath;
get isDrawable() {
return true;
}
constructor(path, time) {
this.filePath = path ?? '';
this.startTime = time ?? 0;
}
}
class StoryboardLayer {
name;
depth;
masking;
visibleWhenPassing = true;
visibleWhenFailing = true;
elements = [];
constructor(params) {
this.name = params.name;
this.depth = params.depth;
this.masking = params.masking ?? true;
}
}
class Storyboard {
variables = new Map();
colors = new BeatmapColorSection();
useSkinSprites = false;
minimumLayerDepth = 0;
fileFormat = 14;
_layers = new Map();
constructor() {
this.addLayer(new StoryboardLayer({ name: 'Video', depth: 4, masking: false }));
this.addLayer(new StoryboardLayer({ name: 'Background', depth: 3 }));
this.addLayer(new StoryboardLayer({ name: 'Fail', depth: 2, visibleWhenPassing: false }));
this.addLayer(new StoryboardLayer({ name: 'Pass', depth: 1, visibleWhenFailing: false }));
this.addLayer(new StoryboardLayer({ name: 'Foreground', depth: 0 }));
this.addLayer(new StoryboardLayer({ name: 'Overlay', depth: -2147483648 }));
}
get layers() {
return this._layers;
}
get hasDrawable() {
for (const layer of this.layers.values()) {
if (layer.elements.find((e) => e.isDrawable)) {
return true;
}
}
return false;
}
get hasVariables() {
for (const _ in this.variables) {
return true;
}
return false;
}
get earliestEventTime() {
let time = Infinity;
this._layers.forEach((layer) => {
const elements = layer.elements;
const min = elements.reduce((m, el) => Math.min(m, el.startTime), 0);
time = Math.min(min, time);
});
return time === Infinity ? null : time;
}
get latestEventTime() {
let time = -Infinity;
this._layers.forEach((layer) => {
const elements = layer.elements;
const max = elements.reduce((max, element) => {
const durationElement = element;
return Math.max(max, durationElement?.endTime ?? element.startTime);
}, 0);
time = Math.max(max, time);
});
return time === -Infinity ? null : time;
}
addLayer(layer) {
if (this._layers.has(layer.name)) {
return;
}
this._layers.set(layer.name, layer);
}
getLayerByType(type) {
return this.getLayerByName(LayerType[type] ?? 'Background');
}
getLayerByName(name) {
const layer = this._layers.get(name)
?? new StoryboardLayer({ name, depth: --this.minimumLayerDepth });
if (!this._layers.has(name)) {
this._layers.set(name, layer);
}
return layer;
}
}
class BeatmapEventSection {
backgroundPath = null;
breaks = [];
storyboard = null;
get isBackgroundReplaced() {
if (!this.backgroundPath || !this.storyboard) {
return false;
}
const filePath = this.backgroundPath.trim().toLowerCase();
const layer = this.storyboard.getLayerByType(LayerType.Background);
return layer.elements.some((e) => e.filePath.toLowerCase() === filePath);
}
clone() {
const cloned = new BeatmapEventSection();
cloned.backgroundPath = this.backgroundPath;
cloned.breaks = this.breaks;
cloned.storyboard = this.storyboard;
return cloned;
}
}
let SampleSet;
(function(SampleSet) {
SampleSet[SampleSet['None'] = 0] = 'None';
SampleSet[SampleSet['Normal'] = 1] = 'Normal';
SampleSet[SampleSet['Soft'] = 2] = 'Soft';
SampleSet[SampleSet['Drum'] = 3] = 'Drum';
})(SampleSet || (SampleSet = {}));
class BeatmapGeneralSection {
audioFilename = '';
audioHash;
overlayPosition = 'NoChange';
skinPreference = '';
audioLeadIn = 0;
previewTime = -1;
countdown = 1;
stackLeniency = 0.7;
countdownOffset = 0;
sampleSet = SampleSet.Normal;
letterboxInBreaks = false;
storyFireInFront;
useSkinSprites = false;
alwaysShowPlayfield;
epilepsyWarning = false;
specialStyle = false;
widescreenStoryboard = false;
samplesMatchPlaybackRate = false;
clone() {
const cloned = new BeatmapGeneralSection();
cloned.audioFilename = this.audioFilename;
if (this.audioHash) {
cloned.audioHash = this.audioHash;
}
cloned.overlayPosition = this.overlayPosition;
cloned.skinPreference = this.skinPreference;
cloned.audioLeadIn = this.audioLeadIn;
cloned.previewTime = this.previewTime;
cloned.countdown = this.countdown;
cloned.stackLeniency = this.stackLeniency;
cloned.countdownOffset = this.countdownOffset;
cloned.sampleSet = this.sampleSet;
cloned.letterboxInBreaks = this.letterboxInBreaks;
if (this.storyFireInFront) {
cloned.storyFireInFront = this.storyFireInFront;
}
cloned.useSkinSprites = this.useSkinSprites;
if (this.alwaysShowPlayfield) {
cloned.alwaysShowPlayfield = this.alwaysShowPlayfield;
}
cloned.epilepsyWarning = this.epilepsyWarning;
cloned.specialStyle = this.specialStyle;
cloned.widescreenStoryboard = this.widescreenStoryboard;
cloned.samplesMatchPlaybackRate = this.samplesMatchPlaybackRate;
return cloned;
}
}
class BeatmapMetadataSection {
title = 'Unknown Title';
artist = 'Unknown Artist';
creator = 'Unknown Creator';
version = 'Normal';
source = '';
tags = [];
beatmapId = 0;
beatmapSetId = 0;
_titleUnicode = 'Unknown Title';
get titleUnicode() {
return this._titleUnicode !== 'Unknown Title'
? this._titleUnicode : this.title;
}
set titleUnicode(value) {
this._titleUnicode = value;
}
_artistUnicode = 'Unknown Artist';
get artistUnicode() {
return this._artistUnicode !== 'Unknown Artist'
? this._artistUnicode : this.artist;
}
set artistUnicode(value) {
this._artistUnicode = value;
}
clone() {
const cloned = new BeatmapMetadataSection();
cloned.title = this.title;
cloned.titleUnicode = this.titleUnicode;
cloned.artist = this.artist;
cloned.artistUnicode = this.artistUnicode;
cloned.creator = this.creator;
cloned.version = this.version;
cloned.source = this.source;
cloned.tags = this.tags.slice();
cloned.beatmapId = this.beatmapId;
cloned.beatmapSetId = this.beatmapSetId;
return cloned;
}
}
class ControlPoint {
group;
constructor(group) {
this.group = group || null;
}
attachGroup(group) {
this.group = group;
}
dettachGroup() {
this.group = null;
}
get startTime() {
if (this.group) {
return this.group.startTime;
}
return 0;
}
}
class ControlPointGroup {
controlPoints = [];
startTime;
constructor(time) {
this.startTime = time;
}
add(point) {
const existing = this.controlPoints.find((p) => {
return p.pointType === point.pointType;
});
if (existing) {
this.remove(existing);
}
point.attachGroup(this);
this.controlPoints.push(point);
}
remove(point) {
const index = this.controlPoints.findIndex((p) => {
return p.pointType === point.pointType;
});
if (index !== -1) {
this.controlPoints.splice(index, 1);
point.dettachGroup();
}
}
}
function findNumber(arr, x) {
let start = 0, mid, end = arr.length - 1;
while (start <= end) {
mid = start + ((end - start) >> 1);
if (arr[mid] === x) {
return mid;
}
if (arr[mid] < x) {
start = mid + 1;
}
else {
end = mid - 1;
}
}
return ~start;
}
function findControlPointIndex(arr, time) {
if (!arr.length) {
return -1;
}
if (time < arr[0].startTime) {
return -1;
}
if (time >= arr[arr.length - 1].startTime) {
return arr.length - 1;
}
let l = 0;
let r = arr.length - 2;
while (l <= r) {
const pivot = l + ((r - l) >> 1);
if (arr[pivot].startTime < time) {
l = pivot + 1;
}
else if (arr[pivot].startTime > time) {
r = pivot - 1;
}
else {
return pivot;
}
}
return l - 1;
}
function findControlPoint(arr, time) {
const index = findControlPointIndex(arr, time);
if (index === -1) {
return null;
}
return arr[index];
}
function findIndex(arr, predicate) {
let l = -1;
let r = arr.length - 1;
while (1 + l < r) {
const mid = l + ((r - l) >> 1);
const cmp = predicate(arr[mid], mid, arr);
cmp ? (r = mid) : (l = mid);
}
return r;
}
let BinarySearch = /* #__PURE__*/Object.freeze({
__proto__: null,
findControlPoint,
findControlPointIndex,
findIndex,
findNumber,
});
function barycentricWeights(points) {
const n = points.length;
const w = [];
for (let i = 0; i < n; i++) {
w[i] = 1;
for (let j = 0; j < n; j++) {
if (i !== j) {
w[i] *= points[i].floatX - points[j].floatX;
}
}
w[i] = 1.0 / w[i];
}
return w;
}
function barycentricLagrange(points, weights, time) {
if (points === null || points.length === 0) {
throw new Error('points must contain at least one point');
}
if (points.length !== weights.length) {
throw new Error('points must contain exactly as many items as weights');
}
let numerator = 0;
let denominator = 0;
for (let i = 0, len = points.length; i < len; ++i) {
if (time === points[i].floatX) {
return points[i].floatY;
}
const li = weights[i] / (time - points[i].floatX);
numerator += li * points[i].floatY;
denominator += li;
}
return numerator / denominator;
}
let Interpolation = /* #__PURE__*/Object.freeze({
__proto__: null,
barycentricLagrange,
barycentricWeights,
});
class FastRandom {
static MAX_INT32 = 2147483647;
static MAX_UINT32 = 4294967295;
static INT_MASK = 0x7fffffff >> 0;
static INT_TO_REAL = 1 / (FastRandom.MAX_INT32 + 1);
_y = 842502087 >>> 0;
_z = 3579807591 >>> 0;
_w = 273326509 >>> 0;
_x = 0;
_bitBuffer = 0;
_bitIndex = 32;
constructor(seed) {
this._x = seed;
}
_next() {
const t = (this._x ^ ((this._x << 11) >>> 0)) >>> 0;
this._x = this._y >>> 0;
this._y = this._z >>> 0;
this._z = this._w >>> 0;
this._w = (this._w ^ (this._w >>> 19)) >>> 0;
this._w = (this._w ^ t) >>> 0;
this._w = (this._w ^ (t >>> 8)) >>> 0;
return this._w;
}
next() {
return (FastRandom.INT_MASK & this._next()) >> 0;
}
nextUInt(lowerBound = 0, upperBound = FastRandom.MAX_INT32) {
if (lowerBound === 0 && upperBound === FastRandom.MAX_INT32) {
return this._next();
}
return (lowerBound + this.nextDouble() * (upperBound - lowerBound)) >>> 0;
}
nextInt(lowerBound = 0, upperBound = FastRandom.MAX_INT32) {
return (lowerBound + this.nextDouble() * (upperBound - lowerBound)) >> 0;
}
nextDouble() {
return FastRandom.INT_TO_REAL * this.next();
}
nextBool() {
if (this._bitIndex === 32) {
this._bitBuffer = this.nextUInt();
this._bitIndex = 1;
return (this._bitBuffer & 1) === 1;
}
this._bitIndex = (this._bitIndex + 1) >> 0;
return ((this._bitBuffer >>= 1) & 1) === 1;
}
}
class RoundHelper {
static PRECISION_ERROR = 1e-15;
static round(x, mode = 1) {
return mode ? this.roundToEven(x) : this.roundAwayFromZero(x);
}
static roundToEven(x) {
return this.isAtMidPoint(x) ? 2 * Math.round(x / 2) : Math.round(x);
}
static roundAwayFromZero(x) {
return this.isAtMidPoint(x) ? (x > 0 ? Math.ceil(x) : Math.floor(x)) : Math.round(x);
}
static isAtMidPoint(x) {
return Math.abs(0.5 - Math.abs(x - (x >> 0))) <= this.PRECISION_ERROR;
}
}
class SortHelper {
static _QUICK_SORT_DEPTH_THRESHOLD = 32;
static _INTRO_SORT_SIZE_THRESHOLD = 16;
static depthSort(keys, comparerFn) {
if (!keys || keys.length === 0) {
return keys;
}
comparerFn ??= this.defaultCompare;
this._depthLimitedQuickSort(keys, 0, keys.length - 1, comparerFn, this._QUICK_SORT_DEPTH_THRESHOLD);
return keys;
}
static introSort(keys, comparerFn) {
if (!keys || keys.length < 2) {
return keys;
}
comparerFn ??= this.defaultCompare;
this._introSort(keys, 0, keys.length - 1, comparerFn, 2 * this._floorLog2(keys.length));
return keys;
}
static _depthLimitedQuickSort(keys, left, right, comparerFn, depthLimit) {
do {
if (depthLimit === 0) {
return this._heapsort(keys, left, right, comparerFn);
}
let i = left;
let j = right;
const middle = i + ((j - i) >> 1);
this._swapIfGreater(keys, comparerFn, i, middle);
this._swapIfGreater(keys, comparerFn, i, j);
this._swapIfGreater(keys, comparerFn, middle, j);
const x = keys[middle];
do {
while (comparerFn(keys[i], x) < 0) {
i++;
}
while (comparerFn(x, keys[j]) < 0) {
j--;
}
if (i > j) {
break;
}
if (i < j) {
[keys[i], keys[j]] = [keys[j], keys[i]];
}
i++;
j--;
} while (i <= j);
depthLimit--;
if (j - left <= right - i) {
if (left < j) {
this._depthLimitedQuickSort(keys, left, j, comparerFn, depthLimit);
}
left = i;
continue;
}
if (i < right) {
this._depthLimitedQuickSort(keys, i, right, comparerFn, depthLimit);
}
right = j;
} while (left < right);
}
static _introSort(keys, left, right, comparerFn, depthLimit) {
while (right > left) {
const partitionSize = right - left + 1;
if (partitionSize <= this._INTRO_SORT_SIZE_THRESHOLD) {
if