2018-01-05 11:21:19 +00:00
|
|
|
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
2017-05-03 03:53:45 +00:00
|
|
|
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
|
|
|
|
2018-02-02 09:47:54 +00:00
|
|
|
|
using System;
|
2017-07-26 04:22:46 +00:00
|
|
|
|
using osu.Game.Beatmaps;
|
2017-12-30 20:23:18 +00:00
|
|
|
|
using osu.Game.Rulesets.Scoring;
|
2017-05-03 03:53:45 +00:00
|
|
|
|
|
2018-02-02 08:52:55 +00:00
|
|
|
|
namespace osu.Game.Rulesets.Objects
|
2017-05-03 03:53:45 +00:00
|
|
|
|
{
|
|
|
|
|
public class HitWindows
|
|
|
|
|
{
|
|
|
|
|
#region Constants
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// PERFECT hit window at OD = 10.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double perfect_min = 27.8;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// PERFECT hit window at OD = 5.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double perfect_mid = 38.8;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// PERFECT hit window at OD = 0.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double perfect_max = 44.8;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// GREAT hit window at OD = 10.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double great_min = 68;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// GREAT hit window at OD = 5.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double great_mid = 98;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// GREAT hit window at OD = 0.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double great_max = 128;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// GOOD hit window at OD = 10.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double good_min = 134;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// GOOD hit window at OD = 5.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double good_mid = 164;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// GOOD hit window at OD = 0.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double good_max = 194;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// OK hit window at OD = 10.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double ok_min = 194;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// OK hit window at OD = 5.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double ok_mid = 224;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// OK hit window at OD = 0.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double ok_max = 254;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2018-02-02 09:56:44 +00:00
|
|
|
|
/// MEH hit window at OD = 10.
|
2017-05-03 03:53:45 +00:00
|
|
|
|
/// </summary>
|
2018-02-02 09:56:44 +00:00
|
|
|
|
private const double meh_min = 242;
|
2017-05-03 03:53:45 +00:00
|
|
|
|
/// <summary>
|
2018-02-02 09:56:44 +00:00
|
|
|
|
/// MEH hit window at OD = 5.
|
2017-05-03 03:53:45 +00:00
|
|
|
|
/// </summary>
|
2018-02-02 09:56:44 +00:00
|
|
|
|
private const double meh_mid = 272;
|
2017-05-03 03:53:45 +00:00
|
|
|
|
/// <summary>
|
2018-02-02 09:56:44 +00:00
|
|
|
|
/// MEH hit window at OD = 0.
|
2017-05-03 03:53:45 +00:00
|
|
|
|
/// </summary>
|
2018-02-02 09:56:44 +00:00
|
|
|
|
private const double meh_max = 302;
|
2017-05-03 03:53:45 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MISS hit window at OD = 10.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double miss_min = 316;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MISS hit window at OD = 5.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double miss_mid = 346;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MISS hit window at OD = 0.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const double miss_max = 376;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Hit window for a PERFECT hit.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public double Perfect = perfect_mid;
|
2017-05-09 11:55:20 +00:00
|
|
|
|
|
2017-05-03 03:53:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Hit window for a GREAT hit.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public double Great = great_mid;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Hit window for a GOOD hit.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public double Good = good_mid;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Hit window for an OK hit.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public double Ok = ok_mid;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2018-02-02 09:56:44 +00:00
|
|
|
|
/// Hit window for a MEH hit.
|
2017-05-03 03:53:45 +00:00
|
|
|
|
/// </summary>
|
2018-02-02 09:56:44 +00:00
|
|
|
|
public double Meh = meh_mid;
|
2017-05-03 03:53:45 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Hit window for a MISS hit.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public double Miss = miss_mid;
|
|
|
|
|
|
2017-05-10 08:29:54 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs default hit windows.
|
|
|
|
|
/// </summary>
|
2017-05-03 03:53:45 +00:00
|
|
|
|
public HitWindows()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-10 08:29:54 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs hit windows by fitting a parameter to a 2-part piecewise linear function for each hit window.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="difficulty">The parameter.</param>
|
2017-05-03 03:53:45 +00:00
|
|
|
|
public HitWindows(double difficulty)
|
|
|
|
|
{
|
|
|
|
|
Perfect = BeatmapDifficulty.DifficultyRange(difficulty, perfect_max, perfect_mid, perfect_min);
|
|
|
|
|
Great = BeatmapDifficulty.DifficultyRange(difficulty, great_max, great_mid, great_min);
|
|
|
|
|
Good = BeatmapDifficulty.DifficultyRange(difficulty, good_max, good_mid, good_min);
|
|
|
|
|
Ok = BeatmapDifficulty.DifficultyRange(difficulty, ok_max, ok_mid, ok_min);
|
2018-02-02 09:56:44 +00:00
|
|
|
|
Meh = BeatmapDifficulty.DifficultyRange(difficulty, meh_max, meh_mid, meh_min);
|
2017-05-03 03:53:45 +00:00
|
|
|
|
Miss = BeatmapDifficulty.DifficultyRange(difficulty, miss_max, miss_mid, miss_min);
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-22 07:28:44 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Retrieves the hit result for a time offset.
|
|
|
|
|
/// </summary>
|
2018-02-02 09:47:54 +00:00
|
|
|
|
/// <param name="timeOffset">The time offset. This should always be a positive value indicating the absolute time offset.</param>
|
|
|
|
|
/// <returns>The hit result, or null if <paramref name="timeOffset"/> doesn't result in a judgement.</returns>
|
|
|
|
|
public HitResult? ResultFor(double timeOffset)
|
2017-05-22 07:28:44 +00:00
|
|
|
|
{
|
2018-02-02 09:47:54 +00:00
|
|
|
|
timeOffset = Math.Abs(timeOffset);
|
|
|
|
|
|
|
|
|
|
if (timeOffset <= Perfect / 2)
|
2017-09-05 10:44:59 +00:00
|
|
|
|
return HitResult.Perfect;
|
2018-02-02 09:47:54 +00:00
|
|
|
|
if (timeOffset <= Great / 2)
|
2017-09-05 10:44:59 +00:00
|
|
|
|
return HitResult.Great;
|
2018-02-02 09:47:54 +00:00
|
|
|
|
if (timeOffset <= Good / 2)
|
2017-09-05 10:44:59 +00:00
|
|
|
|
return HitResult.Good;
|
2018-02-02 09:47:54 +00:00
|
|
|
|
if (timeOffset <= Ok / 2)
|
2017-09-05 10:44:59 +00:00
|
|
|
|
return HitResult.Ok;
|
2018-02-02 09:56:44 +00:00
|
|
|
|
if (timeOffset <= Meh / 2)
|
2017-09-05 10:44:59 +00:00
|
|
|
|
return HitResult.Meh;
|
2018-02-02 09:47:54 +00:00
|
|
|
|
if (timeOffset <= Miss / 2)
|
|
|
|
|
return HitResult.Miss;
|
|
|
|
|
|
2017-05-22 07:28:44 +00:00
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-10 08:29:54 +00:00
|
|
|
|
/// <summary>
|
2018-02-02 09:47:54 +00:00
|
|
|
|
/// Given a time offset, whether the <see cref="HitObject"/> can ever be hit in the future.
|
2018-02-02 09:56:44 +00:00
|
|
|
|
/// This happens if <paramref name="timeOffset"/> > <see cref="Meh"/>.
|
2018-02-02 09:47:54 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="timeOffset">The time offset.</param>
|
|
|
|
|
/// <returns>Whether the <see cref="HitObject"/> can be hit at any point in the future from this time offset.</returns>
|
2018-02-02 09:56:44 +00:00
|
|
|
|
public bool CanBeHit(double timeOffset) => timeOffset <= Meh / 2;
|
2018-02-02 09:47:54 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Multiplies all hit windows by a value.
|
2017-05-10 08:29:54 +00:00
|
|
|
|
/// </summary>
|
2018-02-02 09:47:54 +00:00
|
|
|
|
/// <param name="windows">The hit windows to multiply.</param>
|
2017-05-10 08:29:54 +00:00
|
|
|
|
/// <param name="value">The value to multiply each hit window by.</param>
|
2017-05-03 03:53:45 +00:00
|
|
|
|
public static HitWindows operator *(HitWindows windows, double value)
|
|
|
|
|
{
|
2018-02-02 09:47:54 +00:00
|
|
|
|
windows.Perfect *= value;
|
|
|
|
|
windows.Great *= value;
|
|
|
|
|
windows.Good *= value;
|
|
|
|
|
windows.Ok *= value;
|
2018-02-02 09:56:44 +00:00
|
|
|
|
windows.Meh *= value;
|
2018-02-02 09:47:54 +00:00
|
|
|
|
windows.Miss *= value;
|
|
|
|
|
|
|
|
|
|
return windows;
|
2017-05-03 03:53:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-10 08:29:54 +00:00
|
|
|
|
/// <summary>
|
2018-02-02 09:47:54 +00:00
|
|
|
|
/// Divides all hit windows by a value.
|
2017-05-10 08:29:54 +00:00
|
|
|
|
/// </summary>
|
2018-02-02 09:47:54 +00:00
|
|
|
|
/// <param name="windows">The hit windows to divide.</param>
|
2017-05-10 08:29:54 +00:00
|
|
|
|
/// <param name="value">The value to divide each hit window by.</param>
|
2017-05-03 03:53:45 +00:00
|
|
|
|
public static HitWindows operator /(HitWindows windows, double value)
|
|
|
|
|
{
|
2018-02-02 09:47:54 +00:00
|
|
|
|
windows.Perfect /= value;
|
|
|
|
|
windows.Great /= value;
|
|
|
|
|
windows.Good /= value;
|
|
|
|
|
windows.Ok /= value;
|
2018-02-02 09:56:44 +00:00
|
|
|
|
windows.Meh /= value;
|
2018-02-02 09:47:54 +00:00
|
|
|
|
windows.Miss /= value;
|
|
|
|
|
|
|
|
|
|
return windows;
|
2017-05-03 03:53:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|