mirror of
https://github.com/ppy/osu.git
synced 2026-05-25 08:29:54 +08:00
8e53f47e78
The implementation in `master` was presuming that Hard Rock and Easy worked the same way across all rulesets, but actually, in stable mania, the two mods have special treatment as per https://github.com/peppy/osu-stable-reference/blob/996648fba06baf4e7d2e0b248959399444017895/osu!/GameplayElements/HitObjectManagerMania.cs#L147-L150 The open question here would be what this means for existing scores set on lazer using this mod.
191 lines
6.9 KiB
C#
191 lines
6.9 KiB
C#
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
using System;
|
|
using osu.Game.Beatmaps;
|
|
using osu.Game.Rulesets.Scoring;
|
|
|
|
namespace osu.Game.Rulesets.Mania.Scoring
|
|
{
|
|
public class ManiaHitWindows : HitWindows
|
|
{
|
|
private static readonly DifficultyRange perfect_window_range = new DifficultyRange(22.4D, 19.4D, 13.9D);
|
|
private static readonly DifficultyRange great_window_range = new DifficultyRange(64, 49, 34);
|
|
private static readonly DifficultyRange good_window_range = new DifficultyRange(97, 82, 67);
|
|
private static readonly DifficultyRange ok_window_range = new DifficultyRange(127, 112, 97);
|
|
private static readonly DifficultyRange meh_window_range = new DifficultyRange(151, 136, 121);
|
|
private static readonly DifficultyRange miss_window_range = new DifficultyRange(188, 173, 158);
|
|
|
|
private double speedMultiplier = 1;
|
|
|
|
/// <summary>
|
|
/// Multiplier used to compensate for the playback speed of the track speeding up or slowing down.
|
|
/// The goal of this multiplier is to keep hit windows independent of track speed.
|
|
/// <list type="bullet">
|
|
/// <item>When the track speed is above 1, the hit window ranges are multiplied by <see cref="SpeedMultiplier"/>, because the time elapses faster.</item>
|
|
/// <item>When the track speed is below 1, the hit window ranges are also multiplied by <see cref="SpeedMultiplier"/>, because the time elapses slower.</item>
|
|
/// </list>
|
|
/// </summary>
|
|
public double SpeedMultiplier
|
|
{
|
|
get => speedMultiplier;
|
|
set
|
|
{
|
|
speedMultiplier = value;
|
|
updateWindows();
|
|
}
|
|
}
|
|
|
|
private double difficultyMultiplier = 1;
|
|
|
|
/// <summary>
|
|
/// Multiplier used to make the gameplay more or less difficult.
|
|
/// <list type="bullet">
|
|
/// <item>When the <see cref="DifficultyMultiplier"/> is above 1, the hit windows decrease to make the gameplay harder.</item>
|
|
/// <item>When the <see cref="DifficultyMultiplier"/> is below 1, the hit windows increase to make the gameplay easier.</item>
|
|
/// </list>
|
|
/// </summary>
|
|
public double DifficultyMultiplier
|
|
{
|
|
get => difficultyMultiplier;
|
|
set
|
|
{
|
|
difficultyMultiplier = value;
|
|
updateWindows();
|
|
}
|
|
}
|
|
|
|
private double totalMultiplier => speedMultiplier / difficultyMultiplier;
|
|
|
|
private double overallDifficulty;
|
|
|
|
private bool classicModActive;
|
|
|
|
public bool ClassicModActive
|
|
{
|
|
get => classicModActive;
|
|
set
|
|
{
|
|
classicModActive = value;
|
|
updateWindows();
|
|
}
|
|
}
|
|
|
|
private bool scoreV2Active;
|
|
|
|
public bool ScoreV2Active
|
|
{
|
|
get => scoreV2Active;
|
|
set
|
|
{
|
|
scoreV2Active = value;
|
|
updateWindows();
|
|
}
|
|
}
|
|
|
|
private bool isConvert;
|
|
|
|
public bool IsConvert
|
|
{
|
|
get => isConvert;
|
|
set
|
|
{
|
|
isConvert = value;
|
|
updateWindows();
|
|
}
|
|
}
|
|
|
|
private double perfect;
|
|
private double great;
|
|
private double good;
|
|
private double ok;
|
|
private double meh;
|
|
private double miss;
|
|
|
|
public override bool IsHitResultAllowed(HitResult result)
|
|
{
|
|
switch (result)
|
|
{
|
|
case HitResult.Perfect:
|
|
case HitResult.Great:
|
|
case HitResult.Good:
|
|
case HitResult.Ok:
|
|
case HitResult.Meh:
|
|
case HitResult.Miss:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override void SetDifficulty(double difficulty)
|
|
{
|
|
overallDifficulty = difficulty;
|
|
updateWindows();
|
|
}
|
|
|
|
private void updateWindows()
|
|
{
|
|
if (ClassicModActive && !ScoreV2Active)
|
|
{
|
|
if (IsConvert)
|
|
{
|
|
perfect = Math.Floor(16 * totalMultiplier) + 0.5;
|
|
great = Math.Floor((Math.Round(overallDifficulty) > 4 ? 34 : 47) * totalMultiplier) + 0.5;
|
|
good = Math.Floor((Math.Round(overallDifficulty) > 4 ? 67 : 77) * totalMultiplier) + 0.5;
|
|
ok = Math.Floor(97 * totalMultiplier) + 0.5;
|
|
meh = Math.Floor(121 * totalMultiplier) + 0.5;
|
|
miss = Math.Floor(158 * totalMultiplier) + 0.5;
|
|
}
|
|
else
|
|
{
|
|
double invertedOd = Math.Clamp(10 - overallDifficulty, 0, 10);
|
|
|
|
perfect = Math.Floor(16 * totalMultiplier) + 0.5;
|
|
great = Math.Floor((34 + 3 * invertedOd) * totalMultiplier) + 0.5;
|
|
good = Math.Floor((67 + 3 * invertedOd) * totalMultiplier) + 0.5;
|
|
ok = Math.Floor((97 + 3 * invertedOd) * totalMultiplier) + 0.5;
|
|
meh = Math.Floor((121 + 3 * invertedOd) * totalMultiplier) + 0.5;
|
|
miss = Math.Floor((158 + 3 * invertedOd) * totalMultiplier) + 0.5;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
perfect = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(overallDifficulty, perfect_window_range) * totalMultiplier) + 0.5;
|
|
great = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(overallDifficulty, great_window_range) * totalMultiplier) + 0.5;
|
|
good = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(overallDifficulty, good_window_range) * totalMultiplier) + 0.5;
|
|
ok = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(overallDifficulty, ok_window_range) * totalMultiplier) + 0.5;
|
|
meh = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(overallDifficulty, meh_window_range) * totalMultiplier) + 0.5;
|
|
miss = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(overallDifficulty, miss_window_range) * totalMultiplier) + 0.5;
|
|
}
|
|
}
|
|
|
|
public override double WindowFor(HitResult result)
|
|
{
|
|
switch (result)
|
|
{
|
|
case HitResult.Perfect:
|
|
return perfect;
|
|
|
|
case HitResult.Great:
|
|
return great;
|
|
|
|
case HitResult.Good:
|
|
return good;
|
|
|
|
case HitResult.Ok:
|
|
return ok;
|
|
|
|
case HitResult.Meh:
|
|
return meh;
|
|
|
|
case HitResult.Miss:
|
|
return miss;
|
|
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(result), result, null);
|
|
}
|
|
}
|
|
}
|
|
}
|