mirror of
https://github.com/ppy/osu.git
synced 2026-05-18 14:10:33 +08:00
Merge branch 'master' into pp-dev
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Mods
|
||||
@@ -9,5 +10,12 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
public class CatchModEasy : ModEasyWithExtraLives
|
||||
{
|
||||
public override LocalisableString Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and extra lives!";
|
||||
|
||||
public override void ApplyToDifficulty(BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyToDifficulty(difficulty);
|
||||
|
||||
difficulty.OverallDifficulty *= ADJUST_RATIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
base.ApplyToDifficulty(difficulty);
|
||||
|
||||
difficulty.OverallDifficulty = Math.Min(difficulty.OverallDifficulty * ADJUST_RATIO, 10.0f);
|
||||
difficulty.CircleSize = Math.Min(difficulty.CircleSize * 1.3f, 10.0f); // CS uses a custom 1.3 ratio.
|
||||
difficulty.ApproachRate = Math.Min(difficulty.ApproachRate * ADJUST_RATIO, 10.0f);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
@@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
new object[] { LegacyMods.Key2, new[] { typeof(ManiaModKey2) } },
|
||||
new object[] { LegacyMods.Mirror, new[] { typeof(ManiaModMirror) } },
|
||||
new object[] { LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(ManiaModHardRock), typeof(ManiaModDoubleTime) } },
|
||||
new object[] { LegacyMods.ScoreV2, new[] { typeof(ModScoreV2) } },
|
||||
new object[] { LegacyMods.ScoreV2, new[] { typeof(ManiaModScoreV2) } },
|
||||
};
|
||||
|
||||
[TestCaseSource(nameof(mania_mod_mapping))]
|
||||
|
||||
@@ -9,7 +9,6 @@ using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@@ -521,14 +520,13 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
ScoreInfo = new ScoreInfo
|
||||
{
|
||||
Ruleset = CreateRuleset().RulesetInfo,
|
||||
Mods = [new ModScoreV2()]
|
||||
Mods = [new ManiaModScoreV2()]
|
||||
}
|
||||
};
|
||||
|
||||
RunTest($@"SV2 single note @ OD{overallDifficulty}", beatmap, $@"SV2 {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
|
||||
}
|
||||
|
||||
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
|
||||
[TestCaseSource(nameof(score_v1_non_convert_test_cases))]
|
||||
public void TestHitWindowTreatmentWithScoreV1NonConvert(float overallDifficulty, double hitOffset, HitResult expectedResult)
|
||||
{
|
||||
@@ -556,7 +554,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
RunTest($@"SV1 single note @ OD{overallDifficulty}", beatmap, $@"SV1 {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
|
||||
}
|
||||
|
||||
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
|
||||
[TestCaseSource(nameof(score_v1_convert_test_cases))]
|
||||
public void TestHitWindowTreatmentWithScoreV1Convert(float overallDifficulty, double hitOffset, HitResult expectedResult)
|
||||
{
|
||||
@@ -585,7 +582,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
RunTest($@"SV1 convert single note @ OD{overallDifficulty}", beatmap, $@"SV1 convert {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
|
||||
}
|
||||
|
||||
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
|
||||
[TestCaseSource(nameof(score_v1_non_convert_hard_rock_test_cases))]
|
||||
public void TestHitWindowTreatmentWithScoreV1AndHardRockNonConvert(float overallDifficulty, double hitOffset, HitResult expectedResult)
|
||||
{
|
||||
@@ -614,7 +610,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
RunTest($@"SV1+HR single note @ OD{overallDifficulty}", beatmap, $@"SV1+HR {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
|
||||
}
|
||||
|
||||
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
|
||||
[TestCaseSource(nameof(score_v1_non_convert_easy_test_cases))]
|
||||
public void TestHitWindowTreatmentWithScoreV1AndEasyNonConvert(float overallDifficulty, double hitOffset, HitResult expectedResult)
|
||||
{
|
||||
@@ -643,7 +638,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
RunTest($@"SV1+EZ single note @ OD{overallDifficulty}", beatmap, $@"SV1+EZ {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
|
||||
}
|
||||
|
||||
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
|
||||
[TestCaseSource(nameof(score_v1_non_convert_double_time_test_cases))]
|
||||
public void TestHitWindowTreatmentWithScoreV1AndDoubleTimeNonConvert(float overallDifficulty, double hitOffset, HitResult expectedResult)
|
||||
{
|
||||
@@ -672,7 +666,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
RunTest($@"SV1+DT single note @ OD{overallDifficulty}", beatmap, $@"SV1+DT {hitOffset}ms @ OD{overallDifficulty} = {expectedResult}", score, [expectedResult]);
|
||||
}
|
||||
|
||||
[Ignore("Tests expected to fail until stable's detailed treatment of hit windows in mania is reproduced.")]
|
||||
[TestCaseSource(nameof(score_v1_non_convert_half_time_test_cases))]
|
||||
public void TestHitWindowTreatmentWithScoreV1AndHalfTimeNonConvert(float overallDifficulty, double hitOffset, HitResult expectedResult)
|
||||
{
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
yield return new ManiaModMirror();
|
||||
|
||||
if (mods.HasFlag(LegacyMods.ScoreV2))
|
||||
yield return new ModScoreV2();
|
||||
yield return new ManiaModScoreV2();
|
||||
}
|
||||
|
||||
public override LegacyMods ConvertToLegacyMods(Mod[] mods)
|
||||
@@ -296,7 +296,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
case ModType.System:
|
||||
return new Mod[]
|
||||
{
|
||||
new ModScoreV2(),
|
||||
new ManiaModScoreV2(),
|
||||
};
|
||||
|
||||
default:
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -17,29 +15,21 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
/// <remarks>
|
||||
/// Historically, in osu!mania, hit windows are expected to adjust relative to the gameplay rate such that the real-world hit window remains the same.
|
||||
/// </remarks>
|
||||
public interface IManiaRateAdjustmentMod : IApplicableToDifficulty, IApplicableToHitObject
|
||||
public interface IManiaRateAdjustmentMod : IApplicableToHitObject
|
||||
{
|
||||
BindableNumber<double> SpeedChange { get; }
|
||||
|
||||
HitWindows HitWindows { get; set; }
|
||||
|
||||
void IApplicableToDifficulty.ApplyToDifficulty(BeatmapDifficulty difficulty)
|
||||
{
|
||||
HitWindows = new ManiaHitWindows(SpeedChange.Value);
|
||||
HitWindows.SetDifficulty(difficulty.OverallDifficulty);
|
||||
}
|
||||
|
||||
void IApplicableToHitObject.ApplyToHitObject(HitObject hitObject)
|
||||
{
|
||||
switch (hitObject)
|
||||
{
|
||||
case Note:
|
||||
hitObject.HitWindows = HitWindows;
|
||||
((ManiaHitWindows)hitObject.HitWindows).SpeedMultiplier = SpeedChange.Value;
|
||||
break;
|
||||
|
||||
case HoldNote hold:
|
||||
hold.Head.HitWindows = HitWindows;
|
||||
hold.Tail.HitWindows = HitWindows;
|
||||
((ManiaHitWindows)hold.Head.HitWindows).SpeedMultiplier = SpeedChange.Value;
|
||||
((ManiaHitWindows)hold.Tail.HitWindows).SpeedMultiplier = SpeedChange.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,41 @@
|
||||
// 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 osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModClassic : ModClassic
|
||||
public class ManiaModClassic : ModClassic, IApplicableToBeatmap
|
||||
{
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
bool isConvert = !beatmap.BeatmapInfo.Ruleset.Equals(new ManiaRuleset().RulesetInfo);
|
||||
|
||||
foreach (var ho in beatmap.HitObjects)
|
||||
{
|
||||
switch (ho)
|
||||
{
|
||||
case Note note:
|
||||
{
|
||||
var hitWindows = (ManiaHitWindows)note.HitWindows;
|
||||
hitWindows.IsConvert = isConvert;
|
||||
hitWindows.ClassicModActive = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case HoldNote hold:
|
||||
{
|
||||
var headWindows = (ManiaHitWindows)hold.Head.HitWindows;
|
||||
var tailWindows = (ManiaHitWindows)hold.Tail.HitWindows;
|
||||
headWindows.IsConvert = tailWindows.IsConvert = isConvert;
|
||||
headWindows.ClassicModActive = tailWindows.ClassicModActive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
// 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 osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModDaycore : ModDaycore, IManiaRateAdjustmentMod
|
||||
{
|
||||
public HitWindows HitWindows { get; set; } = new ManiaHitWindows();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
// 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 osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModDoubleTime : ModDoubleTime, IManiaRateAdjustmentMod
|
||||
{
|
||||
public HitWindows HitWindows { get; set; } = new ManiaHitWindows();
|
||||
|
||||
// For now, all rate-increasing mods should be given a 1x multiplier in mania because it doesn't always
|
||||
// make the map harder and is more of a personal preference.
|
||||
// In the future, we can consider adjusting this by experimenting with not applying the hitwindow leniency.
|
||||
|
||||
@@ -2,12 +2,32 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModEasy : ModEasyWithExtraLives
|
||||
public class ManiaModEasy : ModEasyWithExtraLives, IApplicableToHitObject
|
||||
{
|
||||
public override LocalisableString Description => @"More forgiving HP drain, less accuracy required, and extra lives!";
|
||||
|
||||
void IApplicableToHitObject.ApplyToHitObject(HitObject hitObject)
|
||||
{
|
||||
const double multiplier = 1 / 1.4;
|
||||
|
||||
switch (hitObject)
|
||||
{
|
||||
case Note:
|
||||
((ManiaHitWindows)hitObject.HitWindows).DifficultyMultiplier = multiplier;
|
||||
break;
|
||||
|
||||
case HoldNote hold:
|
||||
((ManiaHitWindows)hold.Head.HitWindows).DifficultyMultiplier = multiplier;
|
||||
((ManiaHitWindows)hold.Tail.HitWindows).DifficultyMultiplier = multiplier;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
// 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 osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModHalfTime : ModHalfTime, IManiaRateAdjustmentMod
|
||||
{
|
||||
public HitWindows HitWindows { get; set; } = new ManiaHitWindows();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,33 @@
|
||||
// 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 osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModHardRock : ModHardRock
|
||||
public class ManiaModHardRock : ModHardRock, IApplicableToHitObject
|
||||
{
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => false;
|
||||
|
||||
void IApplicableToHitObject.ApplyToHitObject(HitObject hitObject)
|
||||
{
|
||||
const double multiplier = 1.4;
|
||||
|
||||
switch (hitObject)
|
||||
{
|
||||
case Note:
|
||||
((ManiaHitWindows)hitObject.HitWindows).DifficultyMultiplier = multiplier;
|
||||
break;
|
||||
|
||||
case HoldNote hold:
|
||||
((ManiaHitWindows)hold.Head.HitWindows).DifficultyMultiplier = multiplier;
|
||||
((ManiaHitWindows)hold.Tail.HitWindows).DifficultyMultiplier = multiplier;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,12 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModNightcore : ModNightcore<ManiaHitObject>, IManiaRateAdjustmentMod
|
||||
{
|
||||
public HitWindows HitWindows { get; set; } = new ManiaHitWindows();
|
||||
|
||||
// For now, all rate-increasing mods should be given a 1x multiplier in mania because it doesn't always
|
||||
// make the map any harder and is more of a personal preference.
|
||||
// In the future, we can consider adjusting this by experimenting with not applying the hitwindow leniency.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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 osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModScoreV2 : ModScoreV2, IApplicableToBeatmap
|
||||
{
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
foreach (var ho in beatmap.HitObjects)
|
||||
{
|
||||
switch (ho)
|
||||
{
|
||||
case Note note:
|
||||
{
|
||||
var hitWindows = (ManiaHitWindows)note.HitWindows;
|
||||
hitWindows.ScoreV2Active = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case HoldNote hold:
|
||||
{
|
||||
var headWindows = (ManiaHitWindows)hold.Head.HitWindows;
|
||||
var tailWindows = (ManiaHitWindows)hold.Tail.HitWindows;
|
||||
headWindows.ScoreV2Active = tailWindows.ScoreV2Active = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,84 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
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 readonly double multiplier;
|
||||
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;
|
||||
@@ -25,16 +102,6 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
private double meh;
|
||||
private double miss;
|
||||
|
||||
public ManiaHitWindows()
|
||||
: this(1)
|
||||
{
|
||||
}
|
||||
|
||||
public ManiaHitWindows(double multiplier)
|
||||
{
|
||||
this.multiplier = multiplier;
|
||||
}
|
||||
|
||||
public override bool IsHitResultAllowed(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
@@ -53,12 +120,44 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
|
||||
public override void SetDifficulty(double difficulty)
|
||||
{
|
||||
perfect = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, perfect_window_range) * multiplier) + 0.5;
|
||||
great = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, great_window_range) * multiplier) + 0.5;
|
||||
good = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, good_window_range) * multiplier) + 0.5;
|
||||
ok = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, ok_window_range) * multiplier) + 0.5;
|
||||
meh = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, meh_window_range) * multiplier) + 0.5;
|
||||
miss = Math.Floor(IBeatmapDifficultyInfo.DifficultyRange(difficulty, miss_window_range) * multiplier) + 0.5;
|
||||
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)
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
double preempt = IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
|
||||
overallDifficulty = (80 - greatHitWindow) / 6;
|
||||
overallDifficulty = (79.5 - greatHitWindow) / 6;
|
||||
approachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5;
|
||||
|
||||
double comboBasedEstimatedMissCount = calculateComboBasedEstimatedMissCount(osuAttributes);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
@@ -9,5 +10,12 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public class OsuModEasy : ModEasyWithExtraLives
|
||||
{
|
||||
public override LocalisableString Description => @"Larger circles, more forgiving HP drain, less accuracy required, and extra lives!";
|
||||
|
||||
public override void ApplyToDifficulty(BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyToDifficulty(difficulty);
|
||||
|
||||
difficulty.OverallDifficulty *= ADJUST_RATIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
base.ApplyToDifficulty(difficulty);
|
||||
|
||||
difficulty.OverallDifficulty = Math.Min(difficulty.OverallDifficulty * ADJUST_RATIO, 10.0f);
|
||||
difficulty.CircleSize = Math.Min(difficulty.CircleSize * 1.3f, 10.0f); // CS uses a custom 1.3 ratio.
|
||||
difficulty.ApproachRate = Math.Min(difficulty.ApproachRate * ADJUST_RATIO, 10.0f);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
public override void ApplyToDifficulty(BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyToDifficulty(difficulty);
|
||||
|
||||
difficulty.OverallDifficulty *= ADJUST_RATIO;
|
||||
difficulty.SliderMultiplier *= slider_multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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.Mods;
|
||||
|
||||
@@ -23,6 +24,8 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
public override void ApplyToDifficulty(BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyToDifficulty(difficulty);
|
||||
|
||||
difficulty.OverallDifficulty = Math.Min(difficulty.OverallDifficulty * ADJUST_RATIO, 10.0f);
|
||||
difficulty.SliderMultiplier *= slider_multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,22 +333,32 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
|
||||
#endregion
|
||||
|
||||
#region Source grouping
|
||||
|
||||
[Test]
|
||||
public async Task TestGroupingBySource()
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
var beatmapSets = new List<BeatmapSetInfo>();
|
||||
addBeatmapSet(s => s.Beatmaps[0].Metadata.Source = "Cool Game", beatmapSets, out var beatmapCoolGame);
|
||||
addBeatmapSet(s => s.Beatmaps[0].Metadata.Source = "Cool game", beatmapSets, out var beatmapCoolGameB);
|
||||
addBeatmapSet(s => s.Beatmaps[0].Metadata.Source = "Nice Movie", beatmapSets, out var beatmapNiceMovie);
|
||||
addBeatmapSet(s => s.Beatmaps[0].Metadata.Source = string.Empty, beatmapSets, out var beatmapUnsourced);
|
||||
|
||||
var results = await runGrouping(GroupMode.Source, beatmapSets);
|
||||
assertGroup(results, 0, "Cool Game", new[] { beatmapCoolGame, beatmapCoolGameB }, ref total);
|
||||
assertGroup(results, 1, "Nice Movie", new[] { beatmapNiceMovie }, ref total);
|
||||
assertGroup(results, 2, "Unsourced", new[] { beatmapUnsourced }, ref total);
|
||||
assertTotal(results, total);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static async Task<List<CarouselItem>> runGrouping(GroupMode group, List<BeatmapSetInfo> beatmapSets)
|
||||
{
|
||||
var groupingFilter = new BeatmapCarouselFilterGrouping(() => new FilterCriteria { Group = group });
|
||||
var carouselItems = await groupingFilter.Run(beatmapSets.SelectMany(s => s.Beatmaps.Select(b => new CarouselItem(b))).ToList(), CancellationToken.None);
|
||||
|
||||
// sanity check to ensure no detection of two group items with equal order value.
|
||||
var groups = carouselItems.Select(i => i.Model).OfType<GroupDefinition>();
|
||||
|
||||
foreach (var header in groups)
|
||||
{
|
||||
var sameOrder = groups.FirstOrDefault(g => g != header && g.Order == header.Order);
|
||||
if (sameOrder != null)
|
||||
Assert.Fail($"Detected two groups with equal order number: \"{header.Title}\" vs. \"{sameOrder.Title}\"");
|
||||
}
|
||||
|
||||
return carouselItems;
|
||||
return await groupingFilter.Run(beatmapSets.SelectMany(s => s.Beatmaps.Select(b => new CarouselItem(b))).ToList(), CancellationToken.None);
|
||||
}
|
||||
|
||||
private static void assertGroup(List<CarouselItem> items, int index, string expectedTitle, IEnumerable<BeatmapSetInfo> expectedBeatmapSets, ref int totalItems)
|
||||
|
||||
@@ -10,14 +10,14 @@ namespace osu.Game.Localisation
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.MenuTip";
|
||||
|
||||
/// <summary>
|
||||
/// "Press Ctrl-T anywhere in the game to toggle the toolbar!"
|
||||
/// "Press {0} anywhere in the game to toggle the toolbar!"
|
||||
/// </summary>
|
||||
public static LocalisableString ToggleToolbarShortcut => new TranslatableString(getKey(@"toggle_toolbar_shortcut"), @"Press Ctrl-T anywhere in the game to toggle the toolbar!");
|
||||
public static LocalisableString ToggleToolbarShortcut(LocalisableString keybind) => new TranslatableString(getKey(@"toggle_toolbar_shortcut"), @"Press {0} anywhere in the game to toggle the toolbar!", keybind);
|
||||
|
||||
/// <summary>
|
||||
/// "Press Ctrl-O anywhere in the game to access settings!"
|
||||
/// "Press {0} anywhere in the game to access settings!"
|
||||
/// </summary>
|
||||
public static LocalisableString GameSettingsShortcut => new TranslatableString(getKey(@"game_settings_shortcut"), @"Press Ctrl-O anywhere in the game to access settings!");
|
||||
public static LocalisableString GameSettingsShortcut(LocalisableString keybind) => new TranslatableString(getKey(@"game_settings_shortcut"), @"Press {0} anywhere in the game to access settings!", keybind);
|
||||
|
||||
/// <summary>
|
||||
/// "All settings are dynamic and take effect in real-time. Try changing the skin while watching autoplay!"
|
||||
@@ -40,9 +40,9 @@ namespace osu.Game.Localisation
|
||||
public static LocalisableString ScreenScalingSettings => new TranslatableString(getKey(@"screen_scaling_settings"), @"Try adjusting the ""Screen Scaling"" mode to change your gameplay or UI area, even in fullscreen!");
|
||||
|
||||
/// <summary>
|
||||
/// "What used to be "osu!direct" is available to all users just like on the website. You can access it anywhere using Ctrl-B!"
|
||||
/// "What used to be "osu!direct" is available to all users just like on the website. You can access it anywhere using {0}!"
|
||||
/// </summary>
|
||||
public static LocalisableString FreeOsuDirect => new TranslatableString(getKey(@"free_osu_direct"), @"What used to be ""osu!direct"" is available to all users just like on the website. You can access it anywhere using Ctrl-B!");
|
||||
public static LocalisableString FreeOsuDirect(LocalisableString keybind) => new TranslatableString(getKey(@"free_osu_direct"), @"What used to be ""osu!direct"" is available to all users just like on the website. You can access it anywhere using {0}!", keybind);
|
||||
|
||||
/// <summary>
|
||||
/// "Seeking in replays is available by dragging on the progress bar at the bottom of the screen or by using the left and right arrow keys!"
|
||||
@@ -75,9 +75,9 @@ namespace osu.Game.Localisation
|
||||
public static LocalisableString ToggleAdvancedFPSCounter => new TranslatableString(getKey(@"toggle_advanced_fps_counter"), @"Toggle advanced frame / thread statistics with Ctrl-F11!");
|
||||
|
||||
/// <summary>
|
||||
/// "You can pause during a replay by pressing Space!"
|
||||
/// "You can pause during a replay by pressing {0}!"
|
||||
/// </summary>
|
||||
public static LocalisableString ReplayPausing => new TranslatableString(getKey(@"replay_pausing"), @"You can pause during a replay by pressing Space!");
|
||||
public static LocalisableString ReplayPausing(LocalisableString keybind) => new TranslatableString(getKey(@"replay_pausing"), @"You can pause during a replay by pressing {0}!", keybind);
|
||||
|
||||
/// <summary>
|
||||
/// "Most of the hotkeys in the game are configurable and can be changed to anything you want. Check the bindings panel under input settings!"
|
||||
@@ -85,9 +85,9 @@ namespace osu.Game.Localisation
|
||||
public static LocalisableString ConfigurableHotkeys => new TranslatableString(getKey(@"configurable_hotkeys"), @"Most of the hotkeys in the game are configurable and can be changed to anything you want. Check the bindings panel under input settings!");
|
||||
|
||||
/// <summary>
|
||||
/// "Your gameplay HUD can be customised by using the skin layout editor. Open it at any time via Ctrl-Shift-S!"
|
||||
/// "Your gameplay HUD can be customised by using the skin layout editor. Open it at any time via {0}!"
|
||||
/// </summary>
|
||||
public static LocalisableString SkinEditor => new TranslatableString(getKey(@"skin_editor"), @"Your gameplay HUD can be customised by using the skin layout editor. Open it at any time via Ctrl-Shift-S!");
|
||||
public static LocalisableString SkinEditor(LocalisableString keybind) => new TranslatableString(getKey(@"skin_editor"), @"Your gameplay HUD can be customised by using the skin layout editor. Open it at any time via {0}!", keybind);
|
||||
|
||||
/// <summary>
|
||||
/// "You can create mod presets to make toggling your favourite mod combinations easier!"
|
||||
@@ -100,14 +100,14 @@ namespace osu.Game.Localisation
|
||||
public static LocalisableString ModCustomisationSettings => new TranslatableString(getKey(@"mod_customisation_settings"), @"Many mods have customisation settings that drastically change how they function. Click the Customise button in mod select to view settings!");
|
||||
|
||||
/// <summary>
|
||||
/// "Press Ctrl-Shift-R to switch to a random skin!"
|
||||
/// "Press {0} to switch to a random skin!"
|
||||
/// </summary>
|
||||
public static LocalisableString RandomSkinShortcut => new TranslatableString(getKey(@"random_skin_shortcut"), @"Press Ctrl-Shift-R to switch to a random skin!");
|
||||
public static LocalisableString RandomSkinShortcut(LocalisableString keybind) => new TranslatableString(getKey(@"random_skin_shortcut"), @"Press {0} to switch to a random skin!", keybind);
|
||||
|
||||
/// <summary>
|
||||
/// "While watching a replay, press Ctrl-H to toggle replay settings!"
|
||||
/// "While watching a replay, press {0} to toggle replay settings!"
|
||||
/// </summary>
|
||||
public static LocalisableString ToggleReplaySettingsShortcut => new TranslatableString(getKey(@"toggle_replay_settings_shortcut"), @"While watching a replay, press Ctrl-H to toggle replay settings!");
|
||||
public static LocalisableString ToggleReplaySettingsShortcut(LocalisableString keybind) => new TranslatableString(getKey(@"toggle_replay_settings_shortcut"), @"While watching a replay, press {0} to toggle replay settings!", keybind);
|
||||
|
||||
/// <summary>
|
||||
/// "You can easily copy the mods from scores on a leaderboard by right-clicking on them!"
|
||||
@@ -140,9 +140,9 @@ namespace osu.Game.Localisation
|
||||
public static LocalisableString GlobalStatisticsShortcut => new TranslatableString(getKey(@"global_statistics_shortcut"), @"Take a look under the hood at performance counters and enable verbose performance logging with Ctrl-F2!");
|
||||
|
||||
/// <summary>
|
||||
/// "When your gameplay HUD is hidden, you can press and hold Ctrl to view it temporarily!"
|
||||
/// "When your gameplay HUD is hidden, you can press and hold {0} to view it temporarily!"
|
||||
/// </summary>
|
||||
public static LocalisableString PeekHUDWhenHidden => new TranslatableString(getKey(@"peek_hud_when_hidden"), @"When your gameplay HUD is hidden, you can press and hold Ctrl to view it temporarily!");
|
||||
public static LocalisableString PeekHUDWhenHidden(LocalisableString keybind) => new TranslatableString(getKey(@"peek_hud_when_hidden"), @"When your gameplay HUD is hidden, you can press and hold {0} to view it temporarily!", keybind);
|
||||
|
||||
/// <summary>
|
||||
/// "Drag and drop any image into the skin editor to load it in quickly!"
|
||||
|
||||
@@ -19,13 +19,13 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override bool Ranked => UsesDefaultConfiguration;
|
||||
public override bool ValidForFreestyleAsRequiredMod => true;
|
||||
|
||||
protected const float ADJUST_RATIO = 0.5f;
|
||||
|
||||
public virtual void ApplyToDifficulty(BeatmapDifficulty difficulty)
|
||||
{
|
||||
const float ratio = 0.5f;
|
||||
difficulty.CircleSize *= ratio;
|
||||
difficulty.ApproachRate *= ratio;
|
||||
difficulty.DrainRate *= ratio;
|
||||
difficulty.OverallDifficulty *= ratio;
|
||||
difficulty.CircleSize *= ADJUST_RATIO;
|
||||
difficulty.ApproachRate *= ADJUST_RATIO;
|
||||
difficulty.DrainRate *= ADJUST_RATIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
public virtual void ApplyToDifficulty(BeatmapDifficulty difficulty)
|
||||
{
|
||||
difficulty.DrainRate = Math.Min(difficulty.DrainRate * ADJUST_RATIO, 10.0f);
|
||||
difficulty.OverallDifficulty = Math.Min(difficulty.OverallDifficulty * ADJUST_RATIO, 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
/// This mod is used strictly to mark osu!stable scores set with the "Score V2" mod active.
|
||||
/// It should not be used in any real capacity going forward.
|
||||
/// </remarks>
|
||||
public sealed class ModScoreV2 : Mod
|
||||
public class ModScoreV2 : Mod
|
||||
{
|
||||
public override string Name => "Score V2";
|
||||
public override string Acronym => @"SV2";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -12,6 +13,8 @@ using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Input;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Localisation;
|
||||
@@ -27,6 +30,9 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private Bindable<bool> showMenuTips = null!;
|
||||
|
||||
[Resolved]
|
||||
private RealmKeyBindingStore keyBindingStore { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@@ -97,42 +103,103 @@ namespace osu.Game.Screens.Menu
|
||||
.FadeOutFromOne(2000, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private const int available_tips = 29;
|
||||
|
||||
private LocalisableString getRandomTip()
|
||||
{
|
||||
LocalisableString[] tips =
|
||||
{
|
||||
MenuTipStrings.ToggleToolbarShortcut,
|
||||
MenuTipStrings.GameSettingsShortcut,
|
||||
MenuTipStrings.DynamicSettings,
|
||||
MenuTipStrings.NewFeaturesAreComingOnline,
|
||||
MenuTipStrings.UIScalingSettings,
|
||||
MenuTipStrings.ScreenScalingSettings,
|
||||
MenuTipStrings.FreeOsuDirect,
|
||||
MenuTipStrings.ReplaySeeking,
|
||||
MenuTipStrings.MultithreadingSupport,
|
||||
MenuTipStrings.TryNewMods,
|
||||
MenuTipStrings.EmbeddedWebContent,
|
||||
MenuTipStrings.BeatmapRightClick,
|
||||
MenuTipStrings.TemporaryDeleteOperations,
|
||||
MenuTipStrings.DiscoverPlaylists,
|
||||
MenuTipStrings.ToggleAdvancedFPSCounter,
|
||||
MenuTipStrings.GlobalStatisticsShortcut,
|
||||
MenuTipStrings.ReplayPausing,
|
||||
MenuTipStrings.ConfigurableHotkeys,
|
||||
MenuTipStrings.PeekHUDWhenHidden,
|
||||
MenuTipStrings.SkinEditor,
|
||||
MenuTipStrings.DragAndDropImageInSkinEditor,
|
||||
MenuTipStrings.ModPresets,
|
||||
MenuTipStrings.ModCustomisationSettings,
|
||||
MenuTipStrings.RandomSkinShortcut,
|
||||
MenuTipStrings.ToggleReplaySettingsShortcut,
|
||||
MenuTipStrings.CopyModsFromScore,
|
||||
MenuTipStrings.AutoplayBeatmapShortcut,
|
||||
MenuTipStrings.LazerIsNotAWord,
|
||||
MenuTipStrings.RightMouseAbsoluteScroll,
|
||||
};
|
||||
int tipIndex = RNG.Next(0, available_tips);
|
||||
|
||||
return tips[RNG.Next(0, tips.Length)];
|
||||
switch (tipIndex)
|
||||
{
|
||||
case 0:
|
||||
return MenuTipStrings.ToggleToolbarShortcut(keyBindingStore.GetReadableKeyCombinationsFor(GlobalAction.ToggleToolbar).FirstOrDefault() ?? InputSettingsStrings.ActionHasNoKeyBinding);
|
||||
|
||||
case 1:
|
||||
return MenuTipStrings.GameSettingsShortcut(keyBindingStore.GetReadableKeyCombinationsFor(GlobalAction.ToggleSettings).FirstOrDefault() ?? InputSettingsStrings.ActionHasNoKeyBinding);
|
||||
|
||||
case 2:
|
||||
return MenuTipStrings.DynamicSettings;
|
||||
|
||||
case 3:
|
||||
return MenuTipStrings.NewFeaturesAreComingOnline;
|
||||
|
||||
case 4:
|
||||
return MenuTipStrings.UIScalingSettings;
|
||||
|
||||
case 5:
|
||||
return MenuTipStrings.ScreenScalingSettings;
|
||||
|
||||
case 6:
|
||||
return MenuTipStrings.FreeOsuDirect(keyBindingStore.GetReadableKeyCombinationsFor(GlobalAction.ToggleBeatmapListing).FirstOrDefault() ?? InputSettingsStrings.ActionHasNoKeyBinding);
|
||||
|
||||
case 7:
|
||||
return MenuTipStrings.ReplaySeeking;
|
||||
|
||||
case 8:
|
||||
return MenuTipStrings.MultithreadingSupport;
|
||||
|
||||
case 9:
|
||||
return MenuTipStrings.TryNewMods;
|
||||
|
||||
case 10:
|
||||
return MenuTipStrings.EmbeddedWebContent;
|
||||
|
||||
case 11:
|
||||
return MenuTipStrings.BeatmapRightClick;
|
||||
|
||||
case 12:
|
||||
return MenuTipStrings.TemporaryDeleteOperations;
|
||||
|
||||
case 13:
|
||||
return MenuTipStrings.DiscoverPlaylists;
|
||||
|
||||
case 14:
|
||||
return MenuTipStrings.ToggleAdvancedFPSCounter;
|
||||
|
||||
case 15:
|
||||
return MenuTipStrings.GlobalStatisticsShortcut;
|
||||
|
||||
case 16:
|
||||
return MenuTipStrings.ReplayPausing(keyBindingStore.GetReadableKeyCombinationsFor(GlobalAction.TogglePauseReplay).FirstOrDefault() ?? InputSettingsStrings.ActionHasNoKeyBinding);
|
||||
|
||||
case 17:
|
||||
return MenuTipStrings.ConfigurableHotkeys;
|
||||
|
||||
case 18:
|
||||
return MenuTipStrings.PeekHUDWhenHidden(keyBindingStore.GetReadableKeyCombinationsFor(GlobalAction.HoldForHUD).FirstOrDefault() ?? InputSettingsStrings.ActionHasNoKeyBinding);
|
||||
|
||||
case 19:
|
||||
return MenuTipStrings.SkinEditor(keyBindingStore.GetReadableKeyCombinationsFor(GlobalAction.ToggleSkinEditor).FirstOrDefault() ?? InputSettingsStrings.ActionHasNoKeyBinding);
|
||||
|
||||
case 20:
|
||||
return MenuTipStrings.DragAndDropImageInSkinEditor;
|
||||
|
||||
case 21:
|
||||
return MenuTipStrings.ModPresets;
|
||||
|
||||
case 22:
|
||||
return MenuTipStrings.ModCustomisationSettings;
|
||||
|
||||
case 23:
|
||||
return MenuTipStrings.RandomSkinShortcut(keyBindingStore.GetReadableKeyCombinationsFor(GlobalAction.RandomSkin).FirstOrDefault() ?? InputSettingsStrings.ActionHasNoKeyBinding);
|
||||
|
||||
case 24:
|
||||
return MenuTipStrings.ToggleReplaySettingsShortcut(keyBindingStore.GetReadableKeyCombinationsFor(GlobalAction.ToggleReplaySettings).FirstOrDefault() ?? InputSettingsStrings.ActionHasNoKeyBinding);
|
||||
|
||||
case 25:
|
||||
return MenuTipStrings.CopyModsFromScore;
|
||||
|
||||
case 26:
|
||||
return MenuTipStrings.AutoplayBeatmapShortcut;
|
||||
|
||||
case 27:
|
||||
return MenuTipStrings.LazerIsNotAWord;
|
||||
|
||||
case 28:
|
||||
return MenuTipStrings.RightMouseAbsoluteScroll;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ namespace osu.Game.Screens.Select.Filter
|
||||
// [Description("Favourites")]
|
||||
// Favourites,
|
||||
|
||||
[Description("Last Played")]
|
||||
LastPlayed,
|
||||
|
||||
[Description("Length")]
|
||||
Length,
|
||||
|
||||
@@ -46,8 +49,8 @@ namespace osu.Game.Screens.Select.Filter
|
||||
[Description("Ranked Status")]
|
||||
RankedStatus,
|
||||
|
||||
[Description("Last Played")]
|
||||
LastPlayed,
|
||||
[Description("Source")]
|
||||
Source,
|
||||
|
||||
[Description("Title")]
|
||||
Title,
|
||||
|
||||
@@ -822,9 +822,31 @@ namespace osu.Game.Screens.SelectV2
|
||||
/// <summary>
|
||||
/// Defines a grouping header for a set of carousel items.
|
||||
/// </summary>
|
||||
/// <param name="Order">The order of this group in the carousel, sorted using ascending order.</param>
|
||||
/// <param name="Title">The title of this group.</param>
|
||||
public record GroupDefinition(int Order, string Title);
|
||||
public record GroupDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// The order of this group in the carousel, sorted using ascending order.
|
||||
/// </summary>
|
||||
public int Order { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The title of this group.
|
||||
/// </summary>
|
||||
public string Title { get; }
|
||||
|
||||
private readonly string uncasedTitle;
|
||||
|
||||
public GroupDefinition(int order, string title)
|
||||
{
|
||||
Order = order;
|
||||
Title = title;
|
||||
uncasedTitle = title.ToLowerInvariant();
|
||||
}
|
||||
|
||||
public virtual bool Equals(GroupDefinition? other) => uncasedTitle == other?.uncasedTitle;
|
||||
|
||||
public override int GetHashCode() => HashCode.Combine(uncasedTitle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a grouping header for a set of carousel items grouped by star difficulty.
|
||||
|
||||
@@ -202,6 +202,9 @@ namespace osu.Game.Screens.SelectV2
|
||||
return defineGroupByLength(length);
|
||||
}, items);
|
||||
|
||||
case GroupMode.Source:
|
||||
return getGroupsBy(b => defineGroupBySource(b.BeatmapSet!.Metadata.Source), items);
|
||||
|
||||
// TODO: need implementation
|
||||
//
|
||||
// case GroupMode.Collections:
|
||||
@@ -225,6 +228,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
{
|
||||
return items.GroupBy(i => getGroup((BeatmapInfo)i.Model))
|
||||
.OrderBy(s => s.Key.Order)
|
||||
.ThenBy(s => s.Key.Title)
|
||||
.Select(g => new GroupMapping(g.Key, g.ToList()))
|
||||
.ToList();
|
||||
}
|
||||
@@ -355,6 +359,14 @@ namespace osu.Game.Screens.SelectV2
|
||||
return new GroupDefinition(11, "Over 10 minutes");
|
||||
}
|
||||
|
||||
private GroupDefinition defineGroupBySource(string source)
|
||||
{
|
||||
if (string.IsNullOrEmpty(source))
|
||||
return new GroupDefinition(1, "Unsourced");
|
||||
|
||||
return new GroupDefinition(0, source);
|
||||
}
|
||||
|
||||
private static T? aggregateMax<T>(BeatmapInfo b, Func<BeatmapInfo, T> func)
|
||||
{
|
||||
var beatmaps = b.BeatmapSet!.Beatmaps.Where(bb => !bb.Hidden);
|
||||
|
||||
Reference in New Issue
Block a user