Skip to content

Commit

Permalink
Add HitObject.hitWindow
Browse files Browse the repository at this point in the history
  • Loading branch information
Rian8337 committed Dec 11, 2024
1 parent fa5c13b commit 8ccc868
Show file tree
Hide file tree
Showing 32 changed files with 574 additions and 500 deletions.
34 changes: 34 additions & 0 deletions packages/osu-base/src/beatmap/hitobjects/HitObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { ObjectTypes } from "../../constants/ObjectTypes";
import { SampleBank } from "../../constants/SampleBank";
import { Vector2 } from "../../math/Vector2";
import { CircleSizeCalculator } from "../../utils/CircleSizeCalculator";
import { DroidHitWindow } from "../../utils/DroidHitWindow";
import { HitWindow } from "../../utils/HitWindow";
import { OsuHitWindow } from "../../utils/OsuHitWindow";
import { BeatmapControlPoints } from "../sections/BeatmapControlPoints";
import { BeatmapDifficulty } from "../sections/BeatmapDifficulty";
import { BankHitSampleInfo } from "./BankHitSampleInfo";
Expand Down Expand Up @@ -113,6 +116,11 @@ export abstract class HitObject {
return this._kiai;
}

/**
* The hit window of this hitobject.
*/
hitWindow: HitWindow | null = null;

protected _stackHeight = 0;

/**
Expand Down Expand Up @@ -208,6 +216,12 @@ export abstract class HitObject {
this.startTime + HitObject.controlPointLeniency,
).isKiai;

this.hitWindow ??= this.createHitWindow(mode);

if (this.hitWindow) {
this.hitWindow.overallDifficulty = difficulty.od;
}

this.timePreempt = BeatmapDifficulty.difficultyRange(
difficulty.ar,
HitObject.preemptMax,
Expand Down Expand Up @@ -338,6 +352,26 @@ export abstract class HitObject {
return new BankHitSampleInfo(sampleName, SampleBank.none);
}

/**
* Creates the hit window of this hitobject.
*
* A `null` return means that this hitobject has no hit window and timing errors should not be displayed to the user.
*
* This will only be called if this hitobject's hit window has not been set externally.
*
* @param mode The gamemode to create the hit window for.
* @returns The created hit window.
*/
protected createHitWindow(mode: Modes): HitWindow | null {
switch (mode) {
case Modes.droid:
return new DroidHitWindow();

case Modes.osu:
return new OsuHitWindow();
}
}

/**
* Returns the string representative of the class.
*/
Expand Down
6 changes: 6 additions & 0 deletions packages/osu-base/src/beatmap/hitobjects/Slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { BeatmapDifficulty } from "../sections/BeatmapDifficulty";
import { BeatmapControlPoints } from "../sections/BeatmapControlPoints";
import { MathUtils } from "../../math/MathUtils";
import { Cached } from "../../utils/Cached";
import { HitWindow } from "../../utils/HitWindow";
import { EmptyHitWindow } from "../../utils/EmptyHitWindow";

/**
* Represents a slider in a beatmap.
Expand Down Expand Up @@ -411,6 +413,10 @@ export class Slider extends HitObject {
return Math.floor(progress * this.spanCount);
}

protected override createHitWindow(): HitWindow | null {
return new EmptyHitWindow();
}

private createNestedHitObjects(mode: Modes): void {
this.nestedHitObjects.length = 0;

Expand Down
6 changes: 6 additions & 0 deletions packages/osu-base/src/beatmap/hitobjects/Spinner.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Vector2 } from "../../math/Vector2";
import { EmptyHitWindow } from "../../utils/EmptyHitWindow";
import { HitWindow } from "../../utils/HitWindow";
import { BeatmapControlPoints } from "../sections/BeatmapControlPoints";
import { BankHitSampleInfo } from "./BankHitSampleInfo";
import { HitObject } from "./HitObject";
Expand Down Expand Up @@ -57,6 +59,10 @@ export class Spinner extends HitObject {
return this.position;
}

protected override createHitWindow(): HitWindow | null {
return new EmptyHitWindow();
}

override toString(): string {
return `Position: [${this._position.x}, ${this._position.y}], duration: ${this.duration}`;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { EmptyHitWindow } from "../../../utils/EmptyHitWindow";
import { HitWindow } from "../../../utils/HitWindow";
import { SliderNestedHitObject } from "./SliderNestedHitObject";

/**
* Represents a slider repeat.
*/
export class SliderRepeat extends SliderNestedHitObject {}
export class SliderRepeat extends SliderNestedHitObject {
protected override createHitWindow(): HitWindow | null {
return new EmptyHitWindow();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { EmptyHitWindow } from "../../../utils/EmptyHitWindow";
import { HitWindow } from "../../../utils/HitWindow";
import { SliderNestedHitObject } from "./SliderNestedHitObject";

/**
* Represents the tail of a slider.
*/
export class SliderTail extends SliderNestedHitObject {}
export class SliderTail extends SliderNestedHitObject {
protected override createHitWindow(): HitWindow | null {
return new EmptyHitWindow();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { EmptyHitWindow } from "../../../utils/EmptyHitWindow";
import { HitWindow } from "../../../utils/HitWindow";
import { SliderNestedHitObject } from "./SliderNestedHitObject";

/**
* Represents a slider tick in a slider.
*/
export class SliderTick extends SliderNestedHitObject {}
export class SliderTick extends SliderNestedHitObject {
protected override createHitWindow(): HitWindow | null {
return new EmptyHitWindow();
}
}
4 changes: 4 additions & 0 deletions packages/osu-base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ export * from "./beatmap/timings/ControlPointManager";
export * from "./beatmap/timings/DifficultyControlPoint";
export * from "./beatmap/timings/DifficultyControlPointManager";
export * from "./online/DroidAPIRequestBuilder";
export * from "./utils/DroidHitWindow";
export * from "./constants/Easing";
export * from "./constants/EditorGridSize";
export * from "./beatmap/timings/EffectControlPoint";
export * from "./beatmap/timings/EffectControlPointManager";
export * from "./utils/EmptyHitWindow";
export * from "./math/ErrorFunction";
export * from "./beatmap/hitobjects/FileHitSampleInfo";
export * from "./constants/GameMode";
Expand Down Expand Up @@ -87,11 +89,13 @@ export * from "./math/NormalDistribution";
export * from "./constants/ObjectTypes";
export * from "./utils/OmitType";
export * from "./online/OsuAPIRequestBuilder";
export * from "./utils/OsuHitWindow";
export * from "./utils/PathApproximator";
export * from "./constants/PathType";
export * from "./beatmap/hitobjects/PlaceableHitObject";
export * from "./utils/Playfield";
export * from "./math/Polynomial";
export * from "./utils/PreciseDroidHitWindow";
export * from "./utils/Precision";
export * from "./online/RequestResponse";
export * from "./utils/RGBColor";
Expand Down
23 changes: 22 additions & 1 deletion packages/osu-base/src/mods/ModPrecise.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
import { HitObject } from "../beatmap/hitobjects/HitObject";
import { Slider } from "../beatmap/hitobjects/Slider";
import { Modes } from "../constants/Modes";
import { PreciseDroidHitWindow } from "../utils/PreciseDroidHitWindow";
import { IModApplicableToDroid } from "./IModApplicableToDroid";
import { IModApplicableToHitObject } from "./IModApplicableToHitObject";
import { Mod } from "./Mod";

/**
* Represents the Precise mod.
*/
export class ModPrecise extends Mod implements IModApplicableToDroid {
export class ModPrecise
extends Mod
implements IModApplicableToDroid, IModApplicableToHitObject
{
override readonly acronym = "PR";
override readonly name = "Precise";

readonly droidRanked = true;
readonly droidScoreMultiplier = 1.06;
readonly droidString = "s";
readonly isDroidLegacyMod = false;

applyToHitObject(_: Modes, hitObject: HitObject): void {
if (hitObject instanceof Slider) {
// For sliders, the hit window is enforced in the head - everything else is an instant hit or miss.
hitObject.head.hitWindow = new PreciseDroidHitWindow(
hitObject.head.hitWindow?.overallDifficulty,
);
} else {
hitObject.hitWindow = new PreciseDroidHitWindow(
hitObject.hitWindow?.overallDifficulty,
);
}
}
}
48 changes: 48 additions & 0 deletions packages/osu-base/src/utils/DroidHitWindow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { HitWindow } from "./HitWindow";

/**
* Represents the hit window of osu!droid _without_ the Precise mod.
*/
export class DroidHitWindow extends HitWindow {
/**
* Calculates the overall difficulty value of a great (300) hit window.
*
* @param value The value of the hit window, in milliseconds.
* @returns The overall difficulty value.
*/
static greatWindowToOD(value: number): number {
return 5 - (value - 75) / 5;
}

/**
* Calculates the overall difficulty value of a good (100) hit window.
*
* @param value The value of the hit window, in milliseconds.
* @returns The overall difficulty value.
*/
static okWindowToOD(value: number): number {
return 5 - (value - 150) / 10;
}

/**
* Calculates the overall difficulty value of a meh (50) hit window.
*
* @param value The value of the hit window, in milliseconds.
* @returns The overall difficulty value.
*/
static mehWindowToOD(value: number): number {
return 5 - (value - 250) / 10;
}

override get greatWindow(): number {
return 75 + 5 * (5 - this.overallDifficulty);
}

override get okWindow(): number {
return 150 + 10 * (5 - this.overallDifficulty);
}

override get mehWindow(): number {
return 250 + 10 * (5 - this.overallDifficulty);
}
}
24 changes: 24 additions & 0 deletions packages/osu-base/src/utils/EmptyHitWindow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { HitWindow } from "./HitWindow";

/**
* An empty `HitWindow` that does not have any hit windows.
*
* No time values are provided (meaning instantaneous hit or miss).
*/
export class EmptyHitWindow extends HitWindow {
constructor() {
super(0);
}

override get greatWindow(): number {
return 0;
}

override get okWindow(): number {
return 0;
}

override get mehWindow(): number {
return 0;
}
}
Loading

0 comments on commit 8ccc868

Please sign in to comment.