Compare commits
28 Commits
2025.822.0
...
2025.825.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 6cb99c13c2 | |||
| 96008e06ab | |||
| 590b0a8028 | |||
| 70f7f09a83 | |||
|
|
16343fd7d6 | ||
|
|
acafc06bcc | ||
|
|
5292d4a04e | ||
|
|
d3ae20dd88 | ||
|
|
c852e5854c | ||
|
|
0756c45d70 | ||
|
|
4627c8a859 | ||
|
|
30f7da8f71 | ||
|
|
4b8ff481fd | ||
|
|
a7f1795f98 | ||
|
|
c053cfbf9b | ||
|
|
e47a60f303 | ||
|
|
92016a7d9b | ||
|
|
41885c0fc0 | ||
|
|
e75a6b4010 | ||
|
|
ddce11fbc8 | ||
|
|
c894969d17 | ||
|
|
ad6c0c272d | ||
|
|
bb5933ef80 | ||
|
|
62548244bc | ||
|
|
a393b3c6b1 | ||
|
|
a3443f76be | ||
|
|
d998847271 | ||
|
|
62803af1de |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -148,9 +148,7 @@ jobs:
|
||||
# https://github.com/dotnet/macios/issues/19157
|
||||
# https://github.com/actions/runner-images/issues/12758
|
||||
- name: Use Xcode 16.4
|
||||
run: |
|
||||
sudo xcode-select -switch /Applications/Xcode_16.4.app
|
||||
xcodebuild -downloadPlatform iOS
|
||||
run: sudo xcode-select -switch /Applications/Xcode_16.4.app
|
||||
|
||||
- name: Build
|
||||
run: dotnet build -c Debug osu.iOS.slnf
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
public override string Acronym => "FF";
|
||||
public override LocalisableString Description => "The fruits are... floating?";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Cloud;
|
||||
public override IconUsage? Icon => OsuIcon.ModFloatingFruits;
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
public override LocalisableString Description => "Dashing by default, slow down!";
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Running;
|
||||
public override IconUsage? Icon => OsuIcon.ModMovingFast;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax) };
|
||||
|
||||
private DrawableCatchRuleset drawableRuleset = null!;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -21,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
public override LocalisableString Description => "No more tricky speed changes!";
|
||||
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Equals;
|
||||
public override IconUsage? Icon => OsuIcon.ModConstantSpeed;
|
||||
|
||||
public override ModType Type => ModType.Conversion;
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
@@ -14,6 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public override string Name => "Cover";
|
||||
public override string Acronym => "CO";
|
||||
public override IconUsage? Icon => OsuIcon.ModCover;
|
||||
|
||||
public override LocalisableString Description => @"Decrease the playfield's viewing area.";
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
@@ -13,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override string Name => "Dual Stages";
|
||||
public override string Acronym => "DS";
|
||||
public override LocalisableString Description => @"Double the stages, double the fun!";
|
||||
public override IconUsage? Icon => OsuIcon.ModDualStages;
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
@@ -12,6 +14,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public override string Name => "Fade In";
|
||||
public override string Acronym => "FI";
|
||||
public override IconUsage? Icon => OsuIcon.ModFadeIn;
|
||||
public override LocalisableString Description => @"Keys appear out of nowhere!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool ValidForFreestyleAsRequiredMod => false;
|
||||
|
||||
@@ -9,6 +9,7 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
@@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
public override LocalisableString Description => @"Replaces all hold notes with normal notes.";
|
||||
|
||||
public override IconUsage? Icon => FontAwesome.Solid.DotCircle;
|
||||
public override IconUsage? Icon => OsuIcon.ModHoldOff;
|
||||
|
||||
public override ModType Type => ModType.Conversion;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
public override LocalisableString Description => "Hold the keys. To the beat.";
|
||||
|
||||
public override IconUsage? Icon => FontAwesome.Solid.YinYang;
|
||||
public override IconUsage? Icon => OsuIcon.ModInvert;
|
||||
|
||||
public override ModType Type => ModType.Conversion;
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override int KeyCount => 1;
|
||||
public override string Name => "One Key";
|
||||
public override string Acronym => "1K";
|
||||
public override IconUsage? Icon => OsuIcon.ModOneKey;
|
||||
public override LocalisableString Description => @"Play with one key.";
|
||||
public override bool Ranked => false;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override int KeyCount => 10;
|
||||
public override string Name => "Ten Keys";
|
||||
public override string Acronym => "10K";
|
||||
public override IconUsage? Icon => OsuIcon.ModTenKeys;
|
||||
public override LocalisableString Description => @"Play with ten keys.";
|
||||
public override bool Ranked => false;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override int KeyCount => 2;
|
||||
public override string Name => "Two Keys";
|
||||
public override string Acronym => "2K";
|
||||
public override IconUsage? Icon => OsuIcon.ModTwoKeys;
|
||||
public override LocalisableString Description => @"Play with two keys.";
|
||||
public override bool Ranked => false;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override int KeyCount => 3;
|
||||
public override string Name => "Three Keys";
|
||||
public override string Acronym => "3K";
|
||||
public override IconUsage? Icon => OsuIcon.ModThreeKeys;
|
||||
public override LocalisableString Description => @"Play with three keys.";
|
||||
public override bool Ranked => false;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override int KeyCount => 4;
|
||||
public override string Name => "Four Keys";
|
||||
public override string Acronym => "4K";
|
||||
public override IconUsage? Icon => OsuIcon.ModFourKeys;
|
||||
public override LocalisableString Description => @"Play with four keys.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override int KeyCount => 5;
|
||||
public override string Name => "Five Keys";
|
||||
public override string Acronym => "5K";
|
||||
public override IconUsage? Icon => OsuIcon.ModFiveKeys;
|
||||
public override LocalisableString Description => @"Play with five keys.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override int KeyCount => 6;
|
||||
public override string Name => "Six Keys";
|
||||
public override string Acronym => "6K";
|
||||
public override IconUsage? Icon => OsuIcon.ModSixKeys;
|
||||
public override LocalisableString Description => @"Play with six keys.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override int KeyCount => 7;
|
||||
public override string Name => "Seven Keys";
|
||||
public override string Acronym => "7K";
|
||||
public override IconUsage? Icon => OsuIcon.ModSevenKeys;
|
||||
public override LocalisableString Description => @"Play with seven keys.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override int KeyCount => 8;
|
||||
public override string Name => "Eight Keys";
|
||||
public override string Acronym => "8K";
|
||||
public override IconUsage? Icon => OsuIcon.ModEightKeys;
|
||||
public override LocalisableString Description => @"Play with eight keys.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override int KeyCount => 9;
|
||||
public override string Name => "Nine Keys";
|
||||
public override string Acronym => "9K";
|
||||
public override IconUsage? Icon => OsuIcon.ModNineKeys;
|
||||
public override LocalisableString Description => @"Play with nine keys.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
@@ -26,6 +28,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
public override double ScoreMultiplier => 0.9;
|
||||
|
||||
public override IconUsage? Icon => OsuIcon.ModNoRelease;
|
||||
|
||||
public override ModType Type => ModType.DifficultyReduction;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ManiaModHoldOff) };
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Name => @"Alternate";
|
||||
public override string Acronym => @"AL";
|
||||
public override LocalisableString Description => @"Don't use the same key twice in a row!";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Keyboard;
|
||||
public override IconUsage? Icon => OsuIcon.ModAlternate;
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModSingleTap) }).ToArray();
|
||||
|
||||
protected override bool CheckValidNewAction(OsuAction action) => LastAcceptedAction != action;
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
@@ -19,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Acronym => "AD";
|
||||
public override LocalisableString Description => "Never trust the approach circles...";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override IconUsage? Icon { get; } = FontAwesome.Regular.Circle;
|
||||
public override IconUsage? Icon => OsuIcon.ModApproachDifferent;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles), typeof(OsuModFreezeFrame) };
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override LocalisableString Description => "Play with blinds on your screen.";
|
||||
public override string Acronym => "BL";
|
||||
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Adjust;
|
||||
public override IconUsage? Icon => OsuIcon.ModBlinds;
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
|
||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -21,6 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => "Bloom";
|
||||
public override string Acronym => "BM";
|
||||
public override IconUsage? Icon => OsuIcon.ModBloom;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override LocalisableString Description => "The cursor blooms into.. a larger cursor!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@@ -11,7 +11,9 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@@ -34,6 +36,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public override IconUsage? Icon => OsuIcon.ModBubbles;
|
||||
|
||||
public override ModType Type => ModType.Fun;
|
||||
|
||||
// Compatibility with these seems potentially feasible in the future, blocked for now because they don't work as one would expect
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public override string Acronym => "DF";
|
||||
|
||||
public override IconUsage? Icon => FontAwesome.Solid.CompressArrowsAlt;
|
||||
public override IconUsage? Icon => OsuIcon.ModDeflate;
|
||||
|
||||
public override LocalisableString Description => "Hit them at the right size!";
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@@ -21,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => "Depth";
|
||||
public override string Acronym => "DP";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Cube;
|
||||
public override IconUsage? Icon => OsuIcon.ModDepth;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override LocalisableString Description => "3D. Almost.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@@ -19,6 +21,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public override string Acronym => "FR";
|
||||
|
||||
public override IconUsage? Icon => OsuIcon.ModFreezeFrame;
|
||||
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public override LocalisableString Description => "Burn the notes into your memory.";
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public override string Acronym => "GR";
|
||||
|
||||
public override IconUsage? Icon => FontAwesome.Solid.ArrowsAltV;
|
||||
public override IconUsage? Icon => OsuIcon.ModGrow;
|
||||
|
||||
public override LocalisableString Description => "Hit them at the right size!";
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using osu.Framework.Localisation;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => "Magnetised";
|
||||
public override string Acronym => "MG";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Magnet;
|
||||
public override IconUsage? Icon => OsuIcon.ModMagnetised;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override LocalisableString Description => "No need to chase the circles – your cursor is a magnet!";
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@@ -23,6 +25,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => "Repel";
|
||||
public override string Acronym => "RP";
|
||||
public override IconUsage? Icon => OsuIcon.ModRepel;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override LocalisableString Description => "Hit objects run away!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
@@ -11,6 +13,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => @"Single Tap";
|
||||
public override string Acronym => @"SG";
|
||||
public override IconUsage? Icon => OsuIcon.ModSingleTap;
|
||||
public override LocalisableString Description => @"You must only use one key!";
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAlternate) }).ToArray();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => "Spin In";
|
||||
public override string Acronym => "SI";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Undo;
|
||||
public override IconUsage? Icon => OsuIcon.ModSpinIn;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override LocalisableString Description => "Circles spin in. No approach circles.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@@ -24,6 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => @"Strict Tracking";
|
||||
public override string Acronym => @"ST";
|
||||
public override IconUsage? Icon => OsuIcon.ModStrictTracking;
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
public override LocalisableString Description => @"Once you start a slider, follow precisely or get a miss.";
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Name => "Target Practice";
|
||||
public override string Acronym => "TP";
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override IconUsage? Icon => OsuIcon.ModTarget;
|
||||
public override IconUsage? Icon => OsuIcon.ModTargetPractice;
|
||||
public override LocalisableString Description => @"Practice keeping up with the beat of the song.";
|
||||
public override double ScoreMultiplier => 0.1;
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
using System;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@@ -18,6 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => "Traceable";
|
||||
public override string Acronym => "TC";
|
||||
public override IconUsage? Icon => OsuIcon.ModTraceable;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override LocalisableString Description => "Put your faith in the approach circles...";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => "Transform";
|
||||
public override string Acronym => "TR";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.ArrowsAlt;
|
||||
public override IconUsage? Icon => OsuIcon.ModTransform;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override LocalisableString Description => "Everything rotates. EVERYTHING.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@@ -19,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => "Wiggle";
|
||||
public override string Acronym => "WG";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Certificate;
|
||||
public override IconUsage? Icon => OsuIcon.ModWiggle;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override LocalisableString Description => "They just won't stay still...";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
public override string Acronym => "CS";
|
||||
public override double ScoreMultiplier => 0.9;
|
||||
public override LocalisableString Description => "No more tricky speed changes!";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Equals;
|
||||
public override IconUsage? Icon => OsuIcon.ModConstantSpeed;
|
||||
public override ModType Type => ModType.Conversion;
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
||||
|
||||
@@ -5,10 +5,12 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@@ -21,6 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
public override string Acronym => "SR";
|
||||
public override double ScoreMultiplier => 0.6;
|
||||
public override LocalisableString Description => "Simplify tricky rhythms!";
|
||||
public override IconUsage? Icon => OsuIcon.ModSimplifiedRhythm;
|
||||
public override ModType Type => ModType.DifficultyReduction;
|
||||
|
||||
[SettingSource("1/3 to 1/2 conversion", "Converts 1/3 patterns to 1/2 rhythm.")]
|
||||
|
||||
@@ -6,9 +6,11 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@@ -24,6 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
public override string Name => @"Single Tap";
|
||||
public override string Acronym => @"SG";
|
||||
public override IconUsage? Icon => OsuIcon.ModSingleTap;
|
||||
public override LocalisableString Description => @"One key for dons, one key for kats.";
|
||||
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@@ -16,6 +18,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
public override string Name => "Swap";
|
||||
public override string Acronym => "SW";
|
||||
public override LocalisableString Description => @"Dons become kats, kats become dons";
|
||||
public override IconUsage? Icon => OsuIcon.ModSwap;
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModRandom)).ToArray();
|
||||
|
||||
@@ -66,6 +66,18 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("toggle black background", () => blackBackground?.FadeTo(1 - blackBackground.Alpha, 300, Easing.OutQuint));
|
||||
|
||||
AddSliderStep("leaderboard width", 0, 800, 300, v =>
|
||||
{
|
||||
if (leaderboard.IsNotNull())
|
||||
leaderboard.Width = v;
|
||||
});
|
||||
|
||||
AddSliderStep("leaderboard height", 0, 1000, 300, v =>
|
||||
{
|
||||
if (leaderboard.IsNotNull())
|
||||
leaderboard.Height = v;
|
||||
});
|
||||
|
||||
AddSliderStep("set player score", 50, 1_000_000, 700_000, v => gameplayState.ScoreProcessor.TotalScore.Value = v);
|
||||
}
|
||||
|
||||
@@ -108,6 +120,45 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("wait for 1st spot", () => leaderboard.TrackedScore!.ScorePosition.Value, () => Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLongScores()
|
||||
{
|
||||
AddStep("set scores", () =>
|
||||
{
|
||||
var friend = new APIUser { Username = "Friend", Id = 1337 };
|
||||
|
||||
var api = (DummyAPIAccess)API;
|
||||
|
||||
api.Friends.Clear();
|
||||
api.Friends.Add(new APIRelation
|
||||
{
|
||||
Mutual = true,
|
||||
RelationType = RelationType.Friend,
|
||||
TargetID = friend.OnlineID,
|
||||
TargetUser = friend
|
||||
});
|
||||
|
||||
// this is dodgy but anything less dodgy is a lot of work
|
||||
((Bindable<LeaderboardScores?>)leaderboardManager.Scores).Value = LeaderboardScores.Success(new[]
|
||||
{
|
||||
new ScoreInfo { User = new APIUser { Username = "Top", Id = 2 }, TotalScore = 900_000_000, Accuracy = 0.99, MaxCombo = 999999 },
|
||||
new ScoreInfo { User = new APIUser { Username = "Second", Id = 14 }, TotalScore = 800_000_000, Accuracy = 0.9, MaxCombo = 888888 },
|
||||
new ScoreInfo { User = friend, TotalScore = 700_000_000, Accuracy = 0.88, MaxCombo = 777777 },
|
||||
}, 3, null);
|
||||
});
|
||||
|
||||
createLeaderboard();
|
||||
|
||||
AddStep("set score to 650k", () => gameplayState.ScoreProcessor.TotalScore.Value = 650_000_000);
|
||||
AddUntilStep("wait for 4th spot", () => leaderboard.TrackedScore!.ScorePosition.Value, () => Is.EqualTo(4));
|
||||
AddStep("set score to 750k", () => gameplayState.ScoreProcessor.TotalScore.Value = 750_000_000);
|
||||
AddUntilStep("wait for 3rd spot", () => leaderboard.TrackedScore!.ScorePosition.Value, () => Is.EqualTo(3));
|
||||
AddStep("set score to 850k", () => gameplayState.ScoreProcessor.TotalScore.Value = 850_000_000);
|
||||
AddUntilStep("wait for 2nd spot", () => leaderboard.TrackedScore!.ScorePosition.Value, () => Is.EqualTo(2));
|
||||
AddStep("set score to 950k", () => gameplayState.ScoreProcessor.TotalScore.Value = 950_000_000);
|
||||
AddUntilStep("wait for 1st spot", () => leaderboard.TrackedScore!.ScorePosition.Value, () => Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLayoutWithManyScores()
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -42,7 +43,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
private DialogOverlay dialogOverlay = null!;
|
||||
|
||||
private LeaderboardManager leaderboardManager = null!;
|
||||
private RealmPopulatingOnlineLookupSource lookupSource = null!;
|
||||
|
||||
private readonly IBindable<Screens.SelectV2.SongSelect.BeatmapSetLookupResult?> onlineLookupResult = new Bindable<Screens.SelectV2.SongSelect.BeatmapSetLookupResult?>();
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
@@ -52,7 +54,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
|
||||
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, API));
|
||||
dependencies.Cache(leaderboardManager = new LeaderboardManager());
|
||||
dependencies.Cache(lookupSource = new RealmPopulatingOnlineLookupSource());
|
||||
dependencies.CacheAs(onlineLookupResult);
|
||||
|
||||
Dependencies.Cache(Realm);
|
||||
|
||||
@@ -68,7 +70,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
});
|
||||
|
||||
LoadComponent(leaderboardManager);
|
||||
LoadComponent(lookupSource);
|
||||
|
||||
Child = contentContainer = new OsuContextMenuContainer
|
||||
{
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Screens.SelectV2;
|
||||
|
||||
@@ -18,64 +17,25 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
public partial class TestSceneBeatmapMetadataWedge : SongSelectComponentsTestScene
|
||||
{
|
||||
private APIBeatmapSet? currentOnlineSet;
|
||||
|
||||
private BeatmapMetadataWedge wedge = null!;
|
||||
|
||||
[Cached(typeof(IBindable<Screens.SelectV2.SongSelect.BeatmapSetLookupResult?>))]
|
||||
private Bindable<Screens.SelectV2.SongSelect.BeatmapSetLookupResult?> onlineLookupResult = new Bindable<Screens.SelectV2.SongSelect.BeatmapSetLookupResult?>();
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
var lookupSource = new RealmPopulatingOnlineLookupSource();
|
||||
Child = new DependencyProvidingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CachedDependencies = [(typeof(RealmPopulatingOnlineLookupSource), lookupSource)],
|
||||
Children =
|
||||
[
|
||||
lookupSource,
|
||||
wedge = new BeatmapMetadataWedge
|
||||
Child = wedge = new BeatmapMetadataWedge
|
||||
{
|
||||
State = { Value = Visibility.Visible },
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
AddStep("register request handling", () =>
|
||||
{
|
||||
((DummyAPIAccess)API).HandleRequest = request =>
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
case GetBeatmapSetRequest set:
|
||||
if (set.ID == currentOnlineSet?.OnlineID)
|
||||
{
|
||||
set.TriggerSuccess(currentOnlineSet);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestShowHide()
|
||||
{
|
||||
AddStep("all metrics", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
currentOnlineSet = onlineSet;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddStep("all metrics", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap());
|
||||
|
||||
AddStep("hide wedge", () => wedge.Hide());
|
||||
AddStep("show wedge", () => wedge.Show());
|
||||
@@ -84,67 +44,63 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
[Test]
|
||||
public void TestVariousMetrics()
|
||||
{
|
||||
AddStep("all metrics", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
currentOnlineSet = onlineSet;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddStep("all metrics", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap());
|
||||
|
||||
AddStep("null beatmap", () => Beatmap.SetDefault());
|
||||
AddStep("no source", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
working.Metadata.Source = string.Empty;
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = online;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddStep("no success rate", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
onlineSet.Beatmaps.Single().PlayCount = 0;
|
||||
onlineSet.Beatmaps.Single().PassCount = 0;
|
||||
online.Result!.Beatmaps.Single().PlayCount = 0;
|
||||
online.Result!.Beatmaps.Single().PassCount = 0;
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = online;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddStep("no user ratings", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
onlineSet.Ratings = Array.Empty<int>();
|
||||
online.Result!.Ratings = Array.Empty<int>();
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = online;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddStep("no fail times", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
onlineSet.Beatmaps.Single().FailTimes = null;
|
||||
online.Result!.Beatmaps.Single().FailTimes = null;
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = online;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddStep("no metrics", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
onlineSet.Ratings = Array.Empty<int>();
|
||||
onlineSet.Beatmaps.Single().FailTimes = null;
|
||||
online.Result!.Ratings = Array.Empty<int>();
|
||||
online.Result!.Beatmaps.Single().FailTimes = null;
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = online;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddStep("local beatmap", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, _) = createTestBeatmap();
|
||||
|
||||
working.BeatmapInfo.OnlineID = 0;
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = null;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
}
|
||||
@@ -154,16 +110,16 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
AddStep("long text", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
working.BeatmapInfo.Metadata.Author = new RealmUser { Username = "Verrrrryyyy llooonngggggg author" };
|
||||
working.BeatmapInfo.Metadata.Source = "Verrrrryyyy llooonngggggg source";
|
||||
working.BeatmapInfo.Metadata.Tags = string.Join(' ', Enumerable.Repeat(working.BeatmapInfo.Metadata.Tags, 3));
|
||||
onlineSet.Genre = new BeatmapSetOnlineGenre { Id = 12, Name = "Verrrrryyyy llooonngggggg genre" };
|
||||
onlineSet.Language = new BeatmapSetOnlineLanguage { Id = 12, Name = "Verrrrryyyy llooonngggggg language" };
|
||||
onlineSet.Beatmaps.Single().TopTags = Enumerable.Repeat(onlineSet.Beatmaps.Single().TopTags, 3).SelectMany(t => t!).ToArray();
|
||||
online.Result!.Genre = new BeatmapSetOnlineGenre { Id = 12, Name = "Verrrrryyyy llooonngggggg genre" };
|
||||
online.Result!.Language = new BeatmapSetOnlineLanguage { Id = 12, Name = "Verrrrryyyy llooonngggggg language" };
|
||||
online.Result!.Beatmaps.Single().TopTags = Enumerable.Repeat(online.Result!.Beatmaps.Single().TopTags, 3).SelectMany(t => t!).ToArray();
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = online;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
}
|
||||
@@ -171,22 +127,17 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
[Test]
|
||||
public void TestOnlineAvailability()
|
||||
{
|
||||
AddStep("online beatmapset", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
AddStep("online beatmapset", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap());
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddUntilStep("rating wedge visible", () => wedge.RatingsVisible);
|
||||
AddUntilStep("fail time wedge visible", () => wedge.FailRetryVisible);
|
||||
AddStep("online beatmapset with local diff", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, lookupResult) = createTestBeatmap();
|
||||
|
||||
working.BeatmapInfo.ResetOnlineInfo();
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = lookupResult;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddUntilStep("rating wedge hidden", () => !wedge.RatingsVisible);
|
||||
@@ -195,7 +146,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
var (working, _) = createTestBeatmap();
|
||||
|
||||
currentOnlineSet = null;
|
||||
onlineLookupResult.Value = null;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddAssert("rating wedge still hidden", () => !wedge.RatingsVisible);
|
||||
@@ -205,21 +156,17 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
[Test]
|
||||
public void TestUserTags()
|
||||
{
|
||||
AddStep("user tags", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
AddStep("user tags", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap());
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddStep("no user tags", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
onlineSet.Beatmaps.Single().TopTags = null;
|
||||
onlineSet.RelatedTags = null;
|
||||
online.Result!.Beatmaps.Single().TopTags = null;
|
||||
online.Result!.RelatedTags = null;
|
||||
working.BeatmapSetInfo.Beatmaps.Single().Metadata.UserTags.Clear();
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = online;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
}
|
||||
@@ -227,72 +174,60 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
[Test]
|
||||
public void TestLoading()
|
||||
{
|
||||
AddStep("override request handling", () =>
|
||||
{
|
||||
currentOnlineSet = null;
|
||||
|
||||
((DummyAPIAccess)API).HandleRequest = request =>
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
case GetBeatmapSetRequest set:
|
||||
Scheduler.AddDelayed(() => set.TriggerSuccess(currentOnlineSet!), 500);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("set beatmap", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = Screens.SelectV2.SongSelect.BeatmapSetLookupResult.InProgress();
|
||||
Scheduler.AddDelayed(() => onlineLookupResult.Value = online, 500);
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddWaitStep("wait", 5);
|
||||
|
||||
AddStep("set beatmap", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
onlineSet.RelatedTags![0].Name = "other/tag";
|
||||
onlineSet.RelatedTags[1].Name = "another/tag";
|
||||
onlineSet.RelatedTags[2].Name = "some/tag";
|
||||
online.Result!.RelatedTags![0].Name = "other/tag";
|
||||
online.Result!.RelatedTags[1].Name = "another/tag";
|
||||
online.Result!.RelatedTags[2].Name = "some/tag";
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = Screens.SelectV2.SongSelect.BeatmapSetLookupResult.InProgress();
|
||||
Scheduler.AddDelayed(() => onlineLookupResult.Value = online, 500);
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddWaitStep("wait", 5);
|
||||
|
||||
AddStep("no user tags", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
onlineSet.Beatmaps.Single().TopTags = null;
|
||||
onlineSet.RelatedTags = null;
|
||||
online.Result!.Beatmaps.Single().TopTags = null;
|
||||
online.Result!.RelatedTags = null;
|
||||
working.BeatmapSetInfo.Beatmaps.Single().Metadata.UserTags.Clear();
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = Screens.SelectV2.SongSelect.BeatmapSetLookupResult.InProgress();
|
||||
Scheduler.AddDelayed(() => onlineLookupResult.Value = online, 500);
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddWaitStep("wait", 5);
|
||||
|
||||
AddStep("no user tags", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, online) = createTestBeatmap();
|
||||
|
||||
onlineSet.Beatmaps.Single().TopTags = null;
|
||||
onlineSet.RelatedTags = null;
|
||||
online.Result!.Beatmaps.Single().TopTags = null;
|
||||
online.Result!.RelatedTags = null;
|
||||
working.BeatmapSetInfo.Beatmaps.Single().Metadata.UserTags.Clear();
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
onlineLookupResult.Value = Screens.SelectV2.SongSelect.BeatmapSetLookupResult.InProgress();
|
||||
Scheduler.AddDelayed(() => onlineLookupResult.Value = online, 500);
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddWaitStep("wait", 5);
|
||||
}
|
||||
|
||||
private (WorkingBeatmap, APIBeatmapSet) createTestBeatmap()
|
||||
private (WorkingBeatmap, Screens.SelectV2.SongSelect.BeatmapSetLookupResult) createTestBeatmap()
|
||||
{
|
||||
var working = CreateWorkingBeatmap(Ruleset.Value);
|
||||
var onlineSet = new APIBeatmapSet
|
||||
@@ -346,7 +281,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
|
||||
working.BeatmapSetInfo.DateSubmitted = DateTimeOffset.Now;
|
||||
working.BeatmapSetInfo.DateRanked = DateTimeOffset.Now;
|
||||
return (working, onlineSet);
|
||||
working.Metadata.UserTags.AddRange(onlineSet.RelatedTags.Select(t => t.Name));
|
||||
return (working, Screens.SelectV2.SongSelect.BeatmapSetLookupResult.Completed(onlineSet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
@@ -41,10 +42,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
private BeatmapTitleWedge titleWedge = null!;
|
||||
private BeatmapTitleWedge.DifficultyDisplay difficultyDisplay => titleWedge.ChildrenOfType<BeatmapTitleWedge.DifficultyDisplay>().Single();
|
||||
|
||||
private APIBeatmapSet? currentOnlineSet;
|
||||
|
||||
[Cached]
|
||||
private RealmPopulatingOnlineLookupSource lookupSource = new RealmPopulatingOnlineLookupSource();
|
||||
[Cached(typeof(IBindable<Screens.SelectV2.SongSelect.BeatmapSetLookupResult?>))]
|
||||
private Bindable<Screens.SelectV2.SongSelect.BeatmapSetLookupResult?> onlineLookupResult = new Bindable<Screens.SelectV2.SongSelect.BeatmapSetLookupResult?>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
@@ -58,7 +57,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
lookupSource,
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@@ -142,44 +140,18 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
[Test]
|
||||
public void TestOnlineAvailability()
|
||||
{
|
||||
AddStep("set up request handler", () =>
|
||||
{
|
||||
((DummyAPIAccess)API).HandleRequest = request =>
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
case GetBeatmapSetRequest set:
|
||||
if (set.ID == currentOnlineSet?.OnlineID)
|
||||
{
|
||||
set.TriggerSuccess(currentOnlineSet);
|
||||
return true;
|
||||
}
|
||||
AddStep("online beatmapset", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap());
|
||||
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("online beatmapset", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddUntilStep("play count is 10000", () => this.ChildrenOfType<BeatmapTitleWedge.Statistic>().ElementAt(0).Text.ToString(), () => Is.EqualTo("10,000"));
|
||||
AddUntilStep("favourites count is 2345", () => this.ChildrenOfType<BeatmapTitleWedge.FavouriteButton>().Single().Text.ToString(), () => Is.EqualTo("2,345"));
|
||||
AddStep("online beatmapset with local diff", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
var (working, lookupResult) = createTestBeatmap();
|
||||
|
||||
working.BeatmapInfo.ResetOnlineInfo();
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
Beatmap.Value = working;
|
||||
onlineLookupResult.Value = lookupResult;
|
||||
});
|
||||
AddUntilStep("play count is -", () => this.ChildrenOfType<BeatmapTitleWedge.Statistic>().ElementAt(0).Text.ToString(), () => Is.EqualTo("-"));
|
||||
AddUntilStep("favourites count is 2345", () => this.ChildrenOfType<BeatmapTitleWedge.FavouriteButton>().Single().Text.ToString(), () => Is.EqualTo("2,345"));
|
||||
@@ -187,8 +159,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
var (working, _) = createTestBeatmap();
|
||||
|
||||
currentOnlineSet = null;
|
||||
Beatmap.Value = working;
|
||||
onlineLookupResult.Value = Screens.SelectV2.SongSelect.BeatmapSetLookupResult.Completed(null);
|
||||
});
|
||||
AddUntilStep("play count is -", () => this.ChildrenOfType<BeatmapTitleWedge.Statistic>().ElementAt(0).Text.ToString(), () => Is.EqualTo("-"));
|
||||
AddUntilStep("favourites count is -", () => this.ChildrenOfType<BeatmapTitleWedge.FavouriteButton>().Single().Text.ToString(), () => Is.EqualTo("-"));
|
||||
@@ -205,15 +177,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
case GetBeatmapSetRequest set:
|
||||
if (set.ID == currentOnlineSet?.OnlineID)
|
||||
{
|
||||
set.TriggerSuccess(currentOnlineSet);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case PostBeatmapFavouriteRequest favourite:
|
||||
Task.Run(() =>
|
||||
{
|
||||
@@ -228,13 +191,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("online beatmapset", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
AddStep("online beatmapset", () => (Beatmap.Value, onlineLookupResult.Value) = createTestBeatmap());
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
Beatmap.Value = working;
|
||||
});
|
||||
AddUntilStep("play count is 10000", () => this.ChildrenOfType<BeatmapTitleWedge.Statistic>().ElementAt(0).Text.ToString(), () => Is.EqualTo("10,000"));
|
||||
AddUntilStep("favourites count is 2345", () => this.ChildrenOfType<BeatmapTitleWedge.FavouriteButton>().Single().Text.ToString(), () => Is.EqualTo("2,345"));
|
||||
|
||||
@@ -251,13 +209,13 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
AddStep("click favourite button", () => this.ChildrenOfType<BeatmapTitleWedge.FavouriteButton>().Single().TriggerClick());
|
||||
AddStep("change to another beatmap", () =>
|
||||
{
|
||||
var (working, onlineSet) = createTestBeatmap();
|
||||
onlineSet.FavouriteCount = 9999;
|
||||
onlineSet.HasFavourited = true;
|
||||
working.BeatmapSetInfo.OnlineID = onlineSet.OnlineID = 99999;
|
||||
var (working, online) = createTestBeatmap();
|
||||
online.Result!.FavouriteCount = 9999;
|
||||
online.Result!.HasFavourited = true;
|
||||
working.BeatmapSetInfo.OnlineID = online.Result!.OnlineID = 99999;
|
||||
|
||||
currentOnlineSet = onlineSet;
|
||||
Beatmap.Value = working;
|
||||
onlineLookupResult.Value = online;
|
||||
});
|
||||
AddStep("allow request to complete", () => resetEvent.Set());
|
||||
AddUntilStep("favourites count is 9999", () => this.ChildrenOfType<BeatmapTitleWedge.FavouriteButton>().Single().Text.ToString(), () => Is.EqualTo("9,999"));
|
||||
@@ -268,15 +226,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
case GetBeatmapSetRequest set:
|
||||
if (set.ID == currentOnlineSet?.OnlineID)
|
||||
{
|
||||
set.TriggerSuccess(currentOnlineSet);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case PostBeatmapFavouriteRequest favourite:
|
||||
Task.Run(() =>
|
||||
{
|
||||
@@ -350,7 +299,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
});
|
||||
}
|
||||
|
||||
private (WorkingBeatmap, APIBeatmapSet) createTestBeatmap()
|
||||
private (WorkingBeatmap, Screens.SelectV2.SongSelect.BeatmapSetLookupResult) createTestBeatmap()
|
||||
{
|
||||
var working = CreateWorkingBeatmap(Ruleset.Value);
|
||||
var onlineSet = new APIBeatmapSet
|
||||
@@ -371,7 +320,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
|
||||
working.BeatmapSetInfo.DateSubmitted = DateTimeOffset.Now;
|
||||
working.BeatmapSetInfo.DateRanked = DateTimeOffset.Now;
|
||||
return (working, onlineSet);
|
||||
return (working, Screens.SelectV2.SongSelect.BeatmapSetLookupResult.Completed(onlineSet));
|
||||
}
|
||||
|
||||
private class TestHitObject : ConvertHitObject;
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
@@ -22,6 +24,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
private FillFlowContainer spreadOutFlow = null!;
|
||||
private ModDisplay modDisplay = null!;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesetStore { get; set; } = null!;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
@@ -70,9 +75,26 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
[Test]
|
||||
public void TestShowAllMods()
|
||||
{
|
||||
AddStep("create mod icons", () =>
|
||||
createModIconsForRuleset(0);
|
||||
createModIconsForRuleset(1);
|
||||
createModIconsForRuleset(2);
|
||||
createModIconsForRuleset(3);
|
||||
|
||||
AddStep("toggle selected", () =>
|
||||
{
|
||||
addRange(Ruleset.Value.CreateInstance().CreateAllMods().Select(m =>
|
||||
foreach (var icon in this.ChildrenOfType<ModIcon>())
|
||||
icon.Selected.Toggle();
|
||||
});
|
||||
}
|
||||
|
||||
private void createModIconsForRuleset(int rulesetId)
|
||||
{
|
||||
AddStep($"create mod icons for ruleset {rulesetId}", () =>
|
||||
{
|
||||
spreadOutFlow.Clear();
|
||||
modDisplay.Current.Value = [];
|
||||
|
||||
addRange(rulesetStore.GetRuleset(rulesetId)!.CreateInstance().CreateAllMods().Select(m =>
|
||||
{
|
||||
if (m is OsuModFlashlight fl)
|
||||
fl.FollowDelay.Value = 1245;
|
||||
@@ -89,12 +111,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
return m;
|
||||
}));
|
||||
});
|
||||
|
||||
AddStep("toggle selected", () =>
|
||||
{
|
||||
foreach (var icon in this.ChildrenOfType<ModIcon>())
|
||||
icon.Selected.Toggle();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Framework.Configuration;
|
||||
using osu.Framework.Configuration.Tracking;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
@@ -41,6 +42,8 @@ namespace osu.Game.Configuration
|
||||
SetDefault(OsuSetting.Ruleset, string.Empty);
|
||||
SetDefault(OsuSetting.Skin, SkinInfo.ARGON_SKIN.ToString());
|
||||
|
||||
SetDefault(OsuSetting.MenuCookieColor, Colour4.FromHex(@"ff66ba"));
|
||||
|
||||
SetDefault(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Local);
|
||||
SetDefault(OsuSetting.BeatmapLeaderboardSortMode, LeaderboardSortMode.Score);
|
||||
SetDefault(OsuSetting.BeatmapDetailModsFilter, false);
|
||||
@@ -65,6 +68,7 @@ namespace osu.Game.Configuration
|
||||
|
||||
SetDefault(OsuSetting.ToolbarClockDisplayMode, ToolbarClockDisplayMode.Full);
|
||||
|
||||
SetDefault(OsuSetting.ForceLegacySongSelect, false);
|
||||
SetDefault(OsuSetting.SongSelectBackgroundBlur, false);
|
||||
|
||||
// Online settings
|
||||
@@ -405,6 +409,7 @@ namespace osu.Game.Configuration
|
||||
ChatDisplayHeight,
|
||||
BeatmapListingCardSize,
|
||||
ToolbarClockDisplayMode,
|
||||
ForceLegacySongSelect,
|
||||
SongSelectBackgroundBlur,
|
||||
Version,
|
||||
ShowFirstRunSetup,
|
||||
@@ -412,6 +417,7 @@ namespace osu.Game.Configuration
|
||||
Skin,
|
||||
ScreenshotFormat,
|
||||
ScreenshotCaptureMenuCursor,
|
||||
MenuCookieColor,
|
||||
BeatmapSkins,
|
||||
BeatmapColours,
|
||||
BeatmapHitsounds,
|
||||
|
||||
@@ -558,9 +558,15 @@ namespace osu.Game.Database
|
||||
|
||||
Logger.Log("Querying for beatmap sets that contain missing submission/rank date...");
|
||||
|
||||
// find all ranked beatmap sets with missing date ranked or date submitted that have at least one difficulty ranked as well.
|
||||
// the reason for checking ranked status of the difficulties is that they can be locally modified or unknown too, and for those the lookup is likely to fail.
|
||||
// this is because metadata lookups are primarily based on file hash, so they will fail to match if the beatmap does not match the online version
|
||||
// (which is likely to be the case if the beatmap is locally modified or unknown).
|
||||
// that said, one difficulty in ranked state is enough for the backpopulation to work.
|
||||
HashSet<Guid> beatmapSetIds = realmAccess.Run(r => new HashSet<Guid>(
|
||||
r.All<BeatmapSetInfo>()
|
||||
.Where(b => b.StatusInt > 0 && (b.DateRanked == null || b.DateSubmitted == null))
|
||||
.Filter($@"{nameof(BeatmapSetInfo.StatusInt)} > 0 && ({nameof(BeatmapSetInfo.DateRanked)} == null || {nameof(BeatmapSetInfo.DateSubmitted)} == null) "
|
||||
+ $@"&& ANY {nameof(BeatmapSetInfo.Beatmaps)}.{nameof(BeatmapInfo.StatusInt)} > 0")
|
||||
.AsEnumerable()
|
||||
.Select(b => b.ID)));
|
||||
|
||||
@@ -591,11 +597,7 @@ namespace osu.Game.Database
|
||||
{
|
||||
BeatmapSetInfo beatmapSet = r.Find<BeatmapSetInfo>(id)!;
|
||||
|
||||
// we want any ranked representative of the set.
|
||||
// the reason for checking ranked status of the difficulty is that it can be locally modified,
|
||||
// at which point the lookup will fail - but there might still be another unmodified difficulty on which it will work.
|
||||
if (beatmapSet.Beatmaps.FirstOrDefault(b => b.Status >= BeatmapOnlineStatus.Ranked) is not BeatmapInfo beatmap)
|
||||
return false;
|
||||
var beatmap = beatmapSet.Beatmaps.First(b => b.Status >= BeatmapOnlineStatus.Ranked);
|
||||
|
||||
bool lookupSucceeded = localMetadataSource.TryLookup(beatmap, out var result);
|
||||
|
||||
|
||||
@@ -81,27 +81,6 @@ namespace osu.Game.Graphics
|
||||
public static IconUsage InsaneMania => get(0xe027);
|
||||
public static IconUsage ExpertMania => get(0xe028);
|
||||
|
||||
// mod icons
|
||||
public static IconUsage ModPerfect => get(0xe049);
|
||||
public static IconUsage ModAutopilot => get(0xe03a);
|
||||
public static IconUsage ModAuto => get(0xe03b);
|
||||
public static IconUsage ModCinema => get(0xe03c);
|
||||
public static IconUsage ModDoubleTime => get(0xe03d);
|
||||
public static IconUsage ModEasy => get(0xe03e);
|
||||
public static IconUsage ModFlashlight => get(0xe03f);
|
||||
public static IconUsage ModHalftime => get(0xe040);
|
||||
public static IconUsage ModHardRock => get(0xe041);
|
||||
public static IconUsage ModHidden => get(0xe042);
|
||||
public static IconUsage ModNightcore => get(0xe043);
|
||||
public static IconUsage ModNoFail => get(0xe044);
|
||||
public static IconUsage ModRelax => get(0xe045);
|
||||
public static IconUsage ModSpunOut => get(0xe046);
|
||||
public static IconUsage ModSuddenDeath => get(0xe047);
|
||||
public static IconUsage ModTarget => get(0xe048);
|
||||
|
||||
// Use "Icons/BeatmapDetails/mod-icon" instead
|
||||
// public static IconUsage ModBg => Get(0xe04a);
|
||||
|
||||
#endregion
|
||||
|
||||
#region New single-file-based icons
|
||||
@@ -181,6 +160,88 @@ namespace osu.Game.Graphics
|
||||
public static IconUsage Tortoise => get(OsuIconMapping.Tortoise);
|
||||
public static IconUsage Hare => get(OsuIconMapping.Hare);
|
||||
|
||||
// mod icons
|
||||
|
||||
public static IconUsage ModNoMod => get(OsuIconMapping.ModNoMod);
|
||||
|
||||
/*
|
||||
can be regenerated semi-automatically using osu-web's mod database via
|
||||
|
||||
$ jq -r '.[].Mods[].Name' mods.json | sort | uniq | \
|
||||
sed 's/ //g' | \
|
||||
awk '{print "public static IconUsage Mod" $0 " => get(OsuIconMapping.Mod" $0 ");"}' | pbcopy
|
||||
*/
|
||||
|
||||
public static IconUsage ModAccuracyChallenge => get(OsuIconMapping.ModAccuracyChallenge);
|
||||
public static IconUsage ModAdaptiveSpeed => get(OsuIconMapping.ModAdaptiveSpeed);
|
||||
public static IconUsage ModAlternate => get(OsuIconMapping.ModAlternate);
|
||||
public static IconUsage ModApproachDifferent => get(OsuIconMapping.ModApproachDifferent);
|
||||
public static IconUsage ModAutopilot => get(OsuIconMapping.ModAutopilot);
|
||||
public static IconUsage ModAutoplay => get(OsuIconMapping.ModAutoplay);
|
||||
public static IconUsage ModBarrelRoll => get(OsuIconMapping.ModBarrelRoll);
|
||||
public static IconUsage ModBlinds => get(OsuIconMapping.ModBlinds);
|
||||
public static IconUsage ModBloom => get(OsuIconMapping.ModBloom);
|
||||
public static IconUsage ModBubbles => get(OsuIconMapping.ModBubbles);
|
||||
public static IconUsage ModCinema => get(OsuIconMapping.ModCinema);
|
||||
public static IconUsage ModClassic => get(OsuIconMapping.ModClassic);
|
||||
public static IconUsage ModConstantSpeed => get(OsuIconMapping.ModConstantSpeed);
|
||||
public static IconUsage ModCover => get(OsuIconMapping.ModCover);
|
||||
public static IconUsage ModDaycore => get(OsuIconMapping.ModDaycore);
|
||||
public static IconUsage ModDeflate => get(OsuIconMapping.ModDeflate);
|
||||
public static IconUsage ModDepth => get(OsuIconMapping.ModDepth);
|
||||
public static IconUsage ModDifficultyAdjust => get(OsuIconMapping.ModDifficultyAdjust);
|
||||
public static IconUsage ModDoubleTime => get(OsuIconMapping.ModDoubleTime);
|
||||
public static IconUsage ModDualStages => get(OsuIconMapping.ModDualStages);
|
||||
public static IconUsage ModEasy => get(OsuIconMapping.ModEasy);
|
||||
public static IconUsage ModEightKeys => get(OsuIconMapping.ModEightKeys);
|
||||
public static IconUsage ModFadeIn => get(OsuIconMapping.ModFadeIn);
|
||||
public static IconUsage ModFiveKeys => get(OsuIconMapping.ModFiveKeys);
|
||||
public static IconUsage ModFlashlight => get(OsuIconMapping.ModFlashlight);
|
||||
public static IconUsage ModFloatingFruits => get(OsuIconMapping.ModFloatingFruits);
|
||||
public static IconUsage ModFourKeys => get(OsuIconMapping.ModFourKeys);
|
||||
public static IconUsage ModFreezeFrame => get(OsuIconMapping.ModFreezeFrame);
|
||||
public static IconUsage ModGrow => get(OsuIconMapping.ModGrow);
|
||||
public static IconUsage ModHalfTime => get(OsuIconMapping.ModHalfTime);
|
||||
public static IconUsage ModHardRock => get(OsuIconMapping.ModHardRock);
|
||||
public static IconUsage ModHidden => get(OsuIconMapping.ModHidden);
|
||||
public static IconUsage ModHoldOff => get(OsuIconMapping.ModHoldOff);
|
||||
public static IconUsage ModInvert => get(OsuIconMapping.ModInvert);
|
||||
public static IconUsage ModMagnetised => get(OsuIconMapping.ModMagnetised);
|
||||
public static IconUsage ModMirror => get(OsuIconMapping.ModMirror);
|
||||
public static IconUsage ModMovingFast => get(OsuIconMapping.ModMovingFast);
|
||||
public static IconUsage ModMuted => get(OsuIconMapping.ModMuted);
|
||||
public static IconUsage ModNightcore => get(OsuIconMapping.ModNightcore);
|
||||
public static IconUsage ModNineKeys => get(OsuIconMapping.ModNineKeys);
|
||||
public static IconUsage ModNoFail => get(OsuIconMapping.ModNoFail);
|
||||
public static IconUsage ModNoRelease => get(OsuIconMapping.ModNoRelease);
|
||||
public static IconUsage ModNoScope => get(OsuIconMapping.ModNoScope);
|
||||
public static IconUsage ModOneKey => get(OsuIconMapping.ModOneKey);
|
||||
public static IconUsage ModPerfect => get(OsuIconMapping.ModPerfect);
|
||||
public static IconUsage ModRandom => get(OsuIconMapping.ModRandom);
|
||||
public static IconUsage ModRelax => get(OsuIconMapping.ModRelax);
|
||||
public static IconUsage ModRepel => get(OsuIconMapping.ModRepel);
|
||||
public static IconUsage ModScoreV2 => get(OsuIconMapping.ModScoreV2);
|
||||
public static IconUsage ModSevenKeys => get(OsuIconMapping.ModSevenKeys);
|
||||
public static IconUsage ModSimplifiedRhythm => get(OsuIconMapping.ModSimplifiedRhythm);
|
||||
public static IconUsage ModSingleTap => get(OsuIconMapping.ModSingleTap);
|
||||
public static IconUsage ModSixKeys => get(OsuIconMapping.ModSixKeys);
|
||||
public static IconUsage ModSpinIn => get(OsuIconMapping.ModSpinIn);
|
||||
public static IconUsage ModSpunOut => get(OsuIconMapping.ModSpunOut);
|
||||
public static IconUsage ModStrictTracking => get(OsuIconMapping.ModStrictTracking);
|
||||
public static IconUsage ModSuddenDeath => get(OsuIconMapping.ModSuddenDeath);
|
||||
public static IconUsage ModSwap => get(OsuIconMapping.ModSwap);
|
||||
public static IconUsage ModSynesthesia => get(OsuIconMapping.ModSynesthesia);
|
||||
public static IconUsage ModTargetPractice => get(OsuIconMapping.ModTargetPractice);
|
||||
public static IconUsage ModTenKeys => get(OsuIconMapping.ModTenKeys);
|
||||
public static IconUsage ModThreeKeys => get(OsuIconMapping.ModThreeKeys);
|
||||
public static IconUsage ModTouchDevice => get(OsuIconMapping.ModTouchDevice);
|
||||
public static IconUsage ModTraceable => get(OsuIconMapping.ModTraceable);
|
||||
public static IconUsage ModTransform => get(OsuIconMapping.ModTransform);
|
||||
public static IconUsage ModTwoKeys => get(OsuIconMapping.ModTwoKeys);
|
||||
public static IconUsage ModWiggle => get(OsuIconMapping.ModWiggle);
|
||||
public static IconUsage ModWindDown => get(OsuIconMapping.ModWindDown);
|
||||
public static IconUsage ModWindUp => get(OsuIconMapping.ModWindUp);
|
||||
|
||||
private static IconUsage get(OsuIconMapping glyph) => new IconUsage((char)glyph, FONT_NAME);
|
||||
|
||||
private enum OsuIconMapping
|
||||
@@ -400,6 +461,224 @@ namespace osu.Game.Graphics
|
||||
|
||||
[Description(@"hare")]
|
||||
Hare,
|
||||
|
||||
// mod icons
|
||||
|
||||
[Description(@"Mods/mod-no-mod")]
|
||||
ModNoMod,
|
||||
|
||||
/*
|
||||
rest can be regenerated semi-automatically using osu-web's mod database via
|
||||
$ jq -r '.[].Mods[].Name' mods.json | sort | uniq | \
|
||||
awk '{kebab = $0; gsub(" ", "-", kebab); pascal = $0; gsub(" ", "", pascal); print "[Description(@\"Mods/mod-" tolower(kebab) "\")]\nMod" pascal ",\n" }' | pbcopy
|
||||
*/
|
||||
|
||||
[Description(@"Mods/mod-accuracy-challenge")]
|
||||
ModAccuracyChallenge,
|
||||
|
||||
[Description(@"Mods/mod-adaptive-speed")]
|
||||
ModAdaptiveSpeed,
|
||||
|
||||
[Description(@"Mods/mod-alternate")]
|
||||
ModAlternate,
|
||||
|
||||
[Description(@"Mods/mod-approach-different")]
|
||||
ModApproachDifferent,
|
||||
|
||||
[Description(@"Mods/mod-autopilot")]
|
||||
ModAutopilot,
|
||||
|
||||
[Description(@"Mods/mod-autoplay")]
|
||||
ModAutoplay,
|
||||
|
||||
[Description(@"Mods/mod-barrel-roll")]
|
||||
ModBarrelRoll,
|
||||
|
||||
[Description(@"Mods/mod-blinds")]
|
||||
ModBlinds,
|
||||
|
||||
[Description(@"Mods/mod-bloom")]
|
||||
ModBloom,
|
||||
|
||||
[Description(@"Mods/mod-bubbles")]
|
||||
ModBubbles,
|
||||
|
||||
[Description(@"Mods/mod-cinema")]
|
||||
ModCinema,
|
||||
|
||||
[Description(@"Mods/mod-classic")]
|
||||
ModClassic,
|
||||
|
||||
[Description(@"Mods/mod-constant-speed")]
|
||||
ModConstantSpeed,
|
||||
|
||||
[Description(@"Mods/mod-cover")]
|
||||
ModCover,
|
||||
|
||||
[Description(@"Mods/mod-daycore")]
|
||||
ModDaycore,
|
||||
|
||||
[Description(@"Mods/mod-deflate")]
|
||||
ModDeflate,
|
||||
|
||||
[Description(@"Mods/mod-depth")]
|
||||
ModDepth,
|
||||
|
||||
[Description(@"Mods/mod-difficulty-adjust")]
|
||||
ModDifficultyAdjust,
|
||||
|
||||
[Description(@"Mods/mod-double-time")]
|
||||
ModDoubleTime,
|
||||
|
||||
[Description(@"Mods/mod-dual-stages")]
|
||||
ModDualStages,
|
||||
|
||||
[Description(@"Mods/mod-easy")]
|
||||
ModEasy,
|
||||
|
||||
[Description(@"Mods/mod-eight-keys")]
|
||||
ModEightKeys,
|
||||
|
||||
[Description(@"Mods/mod-fade-in")]
|
||||
ModFadeIn,
|
||||
|
||||
[Description(@"Mods/mod-five-keys")]
|
||||
ModFiveKeys,
|
||||
|
||||
[Description(@"Mods/mod-flashlight")]
|
||||
ModFlashlight,
|
||||
|
||||
[Description(@"Mods/mod-floating-fruits")]
|
||||
ModFloatingFruits,
|
||||
|
||||
[Description(@"Mods/mod-four-keys")]
|
||||
ModFourKeys,
|
||||
|
||||
[Description(@"Mods/mod-freeze-frame")]
|
||||
ModFreezeFrame,
|
||||
|
||||
[Description(@"Mods/mod-grow")]
|
||||
ModGrow,
|
||||
|
||||
[Description(@"Mods/mod-half-time")]
|
||||
ModHalfTime,
|
||||
|
||||
[Description(@"Mods/mod-hard-rock")]
|
||||
ModHardRock,
|
||||
|
||||
[Description(@"Mods/mod-hidden")]
|
||||
ModHidden,
|
||||
|
||||
[Description(@"Mods/mod-hold-off")]
|
||||
ModHoldOff,
|
||||
|
||||
[Description(@"Mods/mod-invert")]
|
||||
ModInvert,
|
||||
|
||||
[Description(@"Mods/mod-magnetised")]
|
||||
ModMagnetised,
|
||||
|
||||
[Description(@"Mods/mod-mirror")]
|
||||
ModMirror,
|
||||
|
||||
[Description(@"Mods/mod-moving-fast")]
|
||||
ModMovingFast,
|
||||
|
||||
[Description(@"Mods/mod-muted")]
|
||||
ModMuted,
|
||||
|
||||
[Description(@"Mods/mod-nightcore")]
|
||||
ModNightcore,
|
||||
|
||||
[Description(@"Mods/mod-nine-keys")]
|
||||
ModNineKeys,
|
||||
|
||||
[Description(@"Mods/mod-no-fail")]
|
||||
ModNoFail,
|
||||
|
||||
[Description(@"Mods/mod-no-release")]
|
||||
ModNoRelease,
|
||||
|
||||
[Description(@"Mods/mod-no-scope")]
|
||||
ModNoScope,
|
||||
|
||||
[Description(@"Mods/mod-one-key")]
|
||||
ModOneKey,
|
||||
|
||||
[Description(@"Mods/mod-perfect")]
|
||||
ModPerfect,
|
||||
|
||||
[Description(@"Mods/mod-random")]
|
||||
ModRandom,
|
||||
|
||||
[Description(@"Mods/mod-relax")]
|
||||
ModRelax,
|
||||
|
||||
[Description(@"Mods/mod-repel")]
|
||||
ModRepel,
|
||||
|
||||
[Description(@"Mods/mod-score-v2")]
|
||||
ModScoreV2,
|
||||
|
||||
[Description(@"Mods/mod-seven-keys")]
|
||||
ModSevenKeys,
|
||||
|
||||
[Description(@"Mods/mod-simplified-rhythm")]
|
||||
ModSimplifiedRhythm,
|
||||
|
||||
[Description(@"Mods/mod-single-tap")]
|
||||
ModSingleTap,
|
||||
|
||||
[Description(@"Mods/mod-six-keys")]
|
||||
ModSixKeys,
|
||||
|
||||
[Description(@"Mods/mod-spin-in")]
|
||||
ModSpinIn,
|
||||
|
||||
[Description(@"Mods/mod-spun-out")]
|
||||
ModSpunOut,
|
||||
|
||||
[Description(@"Mods/mod-strict-tracking")]
|
||||
ModStrictTracking,
|
||||
|
||||
[Description(@"Mods/mod-sudden-death")]
|
||||
ModSuddenDeath,
|
||||
|
||||
[Description(@"Mods/mod-swap")]
|
||||
ModSwap,
|
||||
|
||||
[Description(@"Mods/mod-synesthesia")]
|
||||
ModSynesthesia,
|
||||
|
||||
[Description(@"Mods/mod-target-practice")]
|
||||
ModTargetPractice,
|
||||
|
||||
[Description(@"Mods/mod-ten-keys")]
|
||||
ModTenKeys,
|
||||
|
||||
[Description(@"Mods/mod-three-keys")]
|
||||
ModThreeKeys,
|
||||
|
||||
[Description(@"Mods/mod-touch-device")]
|
||||
ModTouchDevice,
|
||||
|
||||
[Description(@"Mods/mod-traceable")]
|
||||
ModTraceable,
|
||||
|
||||
[Description(@"Mods/mod-transform")]
|
||||
ModTransform,
|
||||
|
||||
[Description(@"Mods/mod-two-keys")]
|
||||
ModTwoKeys,
|
||||
|
||||
[Description(@"Mods/mod-wiggle")]
|
||||
ModWiggle,
|
||||
|
||||
[Description(@"Mods/mod-wind-down")]
|
||||
ModWindDown,
|
||||
|
||||
[Description(@"Mods/mod-wind-up")]
|
||||
ModWindUp,
|
||||
}
|
||||
|
||||
public class OsuIconStore : ITextureStore, ITexturedGlyphLookupStore
|
||||
|
||||
@@ -184,6 +184,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString SelectedMods => new TranslatableString(getKey(@"selected_mods"), @"Selected Mods");
|
||||
|
||||
/// <summary>
|
||||
/// "Use legacy song select (SelectV1)"
|
||||
/// </summary>
|
||||
public static LocalisableString ForceLegacySongSelect => new TranslatableString(getKey(@"force_select_v1"), @"Use legacy song select (SelectV1)");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
@@ -104,6 +106,12 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
||||
backgroundToggle,
|
||||
categoryDropdown,
|
||||
refreshButton,
|
||||
new SettingsColour
|
||||
{
|
||||
LabelText = @"osu! logo colour",
|
||||
Current = config.GetBindable<Colour4>(OsuSetting.MenuCookieColor),
|
||||
ClassicDefault = Colour4.FromHex(@"ff66ba"),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,12 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = UserInterfaceStrings.ForceLegacySongSelect,
|
||||
Current = config.GetBindable<bool>(OsuSetting.ForceLegacySongSelect),
|
||||
ClassicDefault = false
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = UserInterfaceStrings.ShowConvertedBeatmaps,
|
||||
|
||||
@@ -6,8 +6,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Localisation.HUD;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@@ -24,6 +26,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public override LocalisableString Description => "Fail if your accuracy drops too low!";
|
||||
|
||||
public override IconUsage? Icon => OsuIcon.ModAccuracyChallenge;
|
||||
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
|
||||
@@ -6,10 +6,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@@ -27,6 +29,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public override LocalisableString Description => "Let track speed adapt to you.";
|
||||
|
||||
public override IconUsage? Icon => OsuIcon.ModAdaptiveSpeed;
|
||||
|
||||
public override ModType Type => ModType.Fun;
|
||||
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public override string Name => "Autoplay";
|
||||
public override string Acronym => "AT";
|
||||
public override IconUsage? Icon => OsuIcon.ModAuto;
|
||||
public override IconUsage? Icon => OsuIcon.ModAutoplay;
|
||||
public override ModType Type => ModType.Automation;
|
||||
public override LocalisableString Description => "Watch a perfect automated play through the song.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@@ -6,8 +6,10 @@ using System.Collections.Generic;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
@@ -36,6 +38,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public override string Name => "Barrel Roll";
|
||||
public override string Acronym => "BR";
|
||||
public override IconUsage? Icon => OsuIcon.ModBarrelRoll;
|
||||
public override LocalisableString Description => "The whole playfield is on a wheel!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
@@ -14,7 +15,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public override double ScoreMultiplier => 0.96;
|
||||
|
||||
public override IconUsage? Icon => FontAwesome.Solid.History;
|
||||
public override IconUsage? Icon => OsuIcon.ModClassic;
|
||||
|
||||
public override LocalisableString Description => "Feeling nostalgic?";
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays.Settings;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
@@ -14,7 +15,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public override string Name => "Daycore";
|
||||
public override string Acronym => "DC";
|
||||
public override IconUsage? Icon => null;
|
||||
public override IconUsage? Icon => OsuIcon.ModDaycore;
|
||||
public override ModType Type => ModType.DifficultyReduction;
|
||||
public override LocalisableString Description => "Whoaaaaa...";
|
||||
public override bool Ranked => UsesDefaultConfiguration;
|
||||
|
||||
@@ -9,6 +9,7 @@ using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
@@ -22,7 +23,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public override ModType Type => ModType.Conversion;
|
||||
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Hammer;
|
||||
public override IconUsage? Icon => OsuIcon.ModDifficultyAdjust;
|
||||
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public override string Name => "Half Time";
|
||||
public override string Acronym => "HT";
|
||||
public override IconUsage? Icon => OsuIcon.ModHalftime;
|
||||
public override IconUsage? Icon => OsuIcon.ModHalfTime;
|
||||
public override ModType Type => ModType.DifficultyReduction;
|
||||
public override LocalisableString Description => "Less zoom...";
|
||||
public override bool Ranked => SpeedChange.IsDefault;
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract class ModMirror : Mod
|
||||
{
|
||||
public override string Name => "Mirror";
|
||||
public override string Acronym => "MR";
|
||||
public override IconUsage? Icon => OsuIcon.ModMirror;
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override double ScoreMultiplier => 1;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@@ -21,7 +22,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public override string Name => "Muted";
|
||||
public override string Acronym => "MU";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.VolumeMute;
|
||||
public override IconUsage? Icon => OsuIcon.ModMuted;
|
||||
public override LocalisableString Description => "Can you still feel the rhythm without music?";
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
@@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Acronym => "NM";
|
||||
public override LocalisableString Description => "No mods applied.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Ban;
|
||||
public override IconUsage? Icon => OsuIcon.ModNoMod;
|
||||
public override ModType Type => ModType.System;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Name => "No Scope";
|
||||
public override string Acronym => "NS";
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override IconUsage? Icon => FontAwesome.Solid.EyeSlash;
|
||||
public override IconUsage? Icon => OsuIcon.ModNoScope;
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => true;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Name => "Random";
|
||||
public override string Acronym => "RD";
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override IconUsage? Icon => OsuIcon.Dice;
|
||||
public override IconUsage? Icon => OsuIcon.ModRandom;
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))]
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
@@ -13,6 +15,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public override string Name => "Score V2";
|
||||
public override string Acronym => @"SV2";
|
||||
public override IconUsage? Icon => OsuIcon.ModScoreV2;
|
||||
public override ModType Type => ModType.System;
|
||||
public override LocalisableString Description => "Score set on earlier osu! versions with the V2 scoring algorithm active.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
@@ -14,6 +16,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Acronym => "SY";
|
||||
public override LocalisableString Description => "Colours hit objects based on the rhythm.";
|
||||
public override double ScoreMultiplier => 0.8;
|
||||
public override IconUsage? Icon => OsuIcon.ModSynesthesia;
|
||||
public override ModType Type => ModType.Fun;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public sealed override string Name => "Touch Device";
|
||||
public sealed override string Acronym => "TD";
|
||||
public sealed override IconUsage? Icon => OsuIcon.PlayStyleTouch;
|
||||
public sealed override IconUsage? Icon => OsuIcon.ModTouchDevice;
|
||||
public sealed override LocalisableString Description => "Automatically applied to plays on devices with a touchscreen.";
|
||||
public sealed override double ScoreMultiplier => 1;
|
||||
public sealed override ModType Type => ModType.System;
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
@@ -14,7 +15,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Name => "Wind Down";
|
||||
public override string Acronym => "WD";
|
||||
public override LocalisableString Description => "Sloooow doooown...";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleDown;
|
||||
public override IconUsage? Icon => OsuIcon.ModWindDown;
|
||||
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble(1)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
@@ -14,7 +15,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Name => "Wind Up";
|
||||
public override string Acronym => "WU";
|
||||
public override LocalisableString Description => "Can you keep up?";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleUp;
|
||||
public override IconUsage? Icon => OsuIcon.ModWindUp;
|
||||
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble(1)
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
public ReplayInputHandler? ReplayInputHandler { get; set; }
|
||||
|
||||
private double? lastBackwardsSeekLogTime;
|
||||
private int invalidBassTimeLogCount;
|
||||
|
||||
/// <summary>
|
||||
/// The number of CPU milliseconds to spend at most during seek catch-up.
|
||||
@@ -161,11 +161,11 @@ namespace osu.Game.Rulesets.UI
|
||||
//
|
||||
// In testing this triggers *very* rarely even when set to super low values (10 ms). The cases we're worried about involve multi-second jumps.
|
||||
// A difference of more than 500 ms seems like a sane number we should never exceed.
|
||||
if (!allowReferenceClockSeeks && Math.Abs(proposedTime - referenceClock.CurrentTime) > 500)
|
||||
if (!allowReferenceClockSeeks && Math.Abs(proposedTime - referenceClock.CurrentTime) > 1500)
|
||||
{
|
||||
if (lastBackwardsSeekLogTime == null || Math.Abs(Clock.CurrentTime - lastBackwardsSeekLogTime.Value) > 1000)
|
||||
if (invalidBassTimeLogCount < 10)
|
||||
{
|
||||
lastBackwardsSeekLogTime = Clock.CurrentTime;
|
||||
invalidBassTimeLogCount++;
|
||||
Logger.Log("Ignoring likely invalid time value provided by BASS during gameplay");
|
||||
Logger.Log($"- provided: {referenceClock.CurrentTime:N2}");
|
||||
Logger.Log($"- expected: {proposedTime:N2}");
|
||||
@@ -175,6 +175,8 @@ namespace osu.Game.Rulesets.UI
|
||||
return;
|
||||
}
|
||||
|
||||
invalidBassTimeLogCount = 0;
|
||||
|
||||
// if the proposed time is the same as the current time, assume that the clock will continue progressing in the same direction as previously.
|
||||
// this avoids spurious flips in direction from -1 to 1 during rewinds.
|
||||
if (state == PlaybackState.Valid && proposedTime != manualClock.CurrentTime)
|
||||
|
||||
@@ -167,7 +167,13 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(45),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
// the mod icon assets in `osu-resources` are sized such that they are flush with the hexagonal background with no shadow baked in.
|
||||
// the `Icons/BeatmapDetails/mod-icon` asset (of size 135x100) has a shadow and some extra transparent pixels baked in.
|
||||
// the hexagonal background on that asset, excluding its shadow and the transparent pixels, is 131px wide and 92px high.
|
||||
// height is divided by 135 rather than by 100, because this entire component is square-sized.
|
||||
Width = 131 / 135f,
|
||||
Height = 92 / 135f,
|
||||
Icon = FontAwesome.Solid.Question
|
||||
},
|
||||
adjustmentMarker = new Container
|
||||
|
||||
@@ -61,7 +61,6 @@ namespace osu.Game.Rulesets.UI
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Spacing = new Vector2(0, 4),
|
||||
Direction = FillDirection.Vertical,
|
||||
Child = tinySwitch = new ModSwitchTiny(mod)
|
||||
{
|
||||
@@ -79,7 +78,9 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Size = new Vector2(21),
|
||||
Size = new Vector2(37, 26),
|
||||
// arbitrary adjustment for better vertical alignment
|
||||
Margin = new MarginPadding { Top = -1 },
|
||||
Icon = mod.Icon.Value
|
||||
});
|
||||
tinySwitch.Scale = new Vector2(0.3f);
|
||||
|
||||
@@ -39,6 +39,7 @@ using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.OnlinePlay.DailyChallenge;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.SelectV2;
|
||||
using osu.Game.Seasonal;
|
||||
using osuTK;
|
||||
@@ -118,12 +119,15 @@ namespace osu.Game.Screens.Menu
|
||||
[CanBeNull]
|
||||
private IDisposable logoProxy;
|
||||
|
||||
private Bindable<bool> forceSSV1;
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics, AudioManager audio)
|
||||
{
|
||||
holdDelay = config.GetBindable<double>(OsuSetting.UIHoldActivationDelay);
|
||||
loginDisplayed = statics.GetBindable<bool>(Static.LoginOverlayDisplayed);
|
||||
showMobileDisclaimer = config.GetBindable<bool>(OsuSetting.ShowMobileDisclaimer);
|
||||
forceSSV1 = config.GetBindable<bool>(OsuSetting.ForceLegacySongSelect);
|
||||
|
||||
if (host.CanExit)
|
||||
{
|
||||
@@ -479,7 +483,7 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
}
|
||||
|
||||
private void loadSongSelect() => this.Push(new SoloSongSelect());
|
||||
private void loadSongSelect() => this.Push(forceSSV1.Value ? new PlaySongSelect() : new SoloSongSelect());
|
||||
|
||||
private partial class MobileDisclaimerDialog : PopupDialog
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
@@ -20,6 +21,7 @@ using osu.Framework.Input.Events;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
@@ -63,7 +65,10 @@ namespace osu.Game.Screens.Menu
|
||||
protected Sample SampleDownbeat;
|
||||
|
||||
private readonly Container colourAndTriangles;
|
||||
private readonly TrianglesV2 triangles;
|
||||
private Box colourBox;
|
||||
private TrianglesV2 triangles;
|
||||
|
||||
private Bindable<Colour4> logoColour;
|
||||
|
||||
/// <summary>
|
||||
/// Return value decides whether the logo should play its own sample for the click action.
|
||||
@@ -186,10 +191,13 @@ namespace osu.Game.Screens.Menu
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
colourBox = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex(@"ff66ab"), Color4Extensions.FromHex(@"cc5289")),
|
||||
Colour = ColourInfo.GradientVertical(
|
||||
Color4Extensions.FromHex(@"ff66ba"), // original osu! cookie pink
|
||||
Color4Extensions.Darken(Color4Extensions.FromHex(@"ff66ba"), 1.0f)
|
||||
),
|
||||
},
|
||||
triangles = new TrianglesV2
|
||||
{
|
||||
@@ -198,7 +206,10 @@ namespace osu.Game.Screens.Menu
|
||||
Thickness = 0.009f,
|
||||
ScaleAdjust = 3,
|
||||
SpawnRatio = 1.4f,
|
||||
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex(@"ff66ab"), Color4Extensions.FromHex(@"b6346f")),
|
||||
Colour = ColourInfo.GradientVertical(
|
||||
Color4Extensions.FromHex(@"ff66ba"),
|
||||
Color4Extensions.Darken(Color4Extensions.FromHex(@"ff66ba"), 2.5f)
|
||||
),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
}
|
||||
@@ -249,6 +260,21 @@ namespace osu.Game.Screens.Menu
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateColour() {
|
||||
if (triangles == null || colourBox == null)
|
||||
return; // we're still loading
|
||||
|
||||
triangles.Colour = ColourInfo.GradientVertical(
|
||||
logoColour.Value,
|
||||
Color4Extensions.Darken(logoColour.Value, 1.0f)
|
||||
);
|
||||
|
||||
colourBox.Colour = ColourInfo.GradientVertical(
|
||||
logoColour.Value,
|
||||
Color4Extensions.Darken(logoColour.Value, 2.5f)
|
||||
);
|
||||
}
|
||||
|
||||
public Container LogoElements { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -276,7 +302,7 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures, AudioManager audio)
|
||||
private void load(TextureStore textures, AudioManager audio, OsuConfigManager config)
|
||||
{
|
||||
sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
|
||||
|
||||
@@ -285,6 +311,8 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
logo.Texture = textures.Get(@"Menu/logo");
|
||||
ripple.Texture = textures.Get(@"Menu/logo");
|
||||
|
||||
logoColour = config.GetBindable<Colour4>(OsuSetting.MenuCookieColor);
|
||||
}
|
||||
|
||||
private int lastBeatIndex;
|
||||
@@ -365,6 +393,7 @@ namespace osu.Game.Screens.Menu
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
UpdateColour();
|
||||
|
||||
const float scale_adjust_cutoff = 0.4f;
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
// Extra lenience is applied so the scores don't get cut off from the left due to elastic easing transforms.
|
||||
float xOffset = DrawableGameplayLeaderboardScore.SHEAR_WIDTH + DrawableGameplayLeaderboardScore.ELASTIC_WIDTH_LENIENCE;
|
||||
|
||||
Width = DrawableGameplayLeaderboardScore.EXTENDED_WIDTH + xOffset;
|
||||
Width = 260 + xOffset;
|
||||
Height = 300;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
@@ -155,6 +155,10 @@ namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
base.Update();
|
||||
|
||||
// limit leaderboard dimensions to a sane minimum.
|
||||
Width = Math.Max(Width, Flow.X + DrawableGameplayLeaderboardScore.MIN_WIDTH);
|
||||
Height = Math.Max(Height, DrawableGameplayLeaderboardScore.PANEL_HEIGHT);
|
||||
|
||||
requiresScroll = Flow.DrawHeight > Height;
|
||||
|
||||
if (requiresScroll && TrackedScore != null)
|
||||
|
||||
@@ -10,6 +10,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Layout;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@@ -26,13 +27,15 @@ namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
public partial class DrawableGameplayLeaderboardScore : CompositeDrawable
|
||||
{
|
||||
public const float EXTENDED_WIDTH = extended_left_panel_width + right_panel_width;
|
||||
public const float MIN_WIDTH = extended_left_panel_width + avatar_size / 2 + 5;
|
||||
|
||||
private const float left_panel_extension_width = 20;
|
||||
|
||||
private const float regular_left_panel_width = avatar_size + avatar_size / 2;
|
||||
private const float extended_left_panel_width = regular_left_panel_width + left_panel_extension_width;
|
||||
private const float right_panel_width = 180;
|
||||
|
||||
private const float accuracy_combo_width_cutoff = 150;
|
||||
private const float username_score_width_cutoff = 50;
|
||||
|
||||
private const float avatar_size = PANEL_HEIGHT;
|
||||
|
||||
@@ -98,6 +101,8 @@ namespace osu.Game.Screens.Play.HUD
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="DrawableGameplayLeaderboardScore"/>.
|
||||
/// </summary>
|
||||
@@ -116,10 +121,12 @@ namespace osu.Game.Screens.Play.HUD
|
||||
if (score.TeamColour != null)
|
||||
BackgroundColour = score.TeamColour.Value;
|
||||
|
||||
AutoSizeAxes = Axes.X;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = PANEL_HEIGHT;
|
||||
|
||||
Shear = OsuGame.SHEAR;
|
||||
|
||||
AddLayout(drawSizeLayout);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -198,7 +205,6 @@ namespace osu.Game.Screens.Play.HUD
|
||||
},
|
||||
rightLayer = new Container
|
||||
{
|
||||
Width = right_panel_width,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
// negative left margin to make the X position of the right layer directly at the avatar center (rendered behind it).
|
||||
Margin = new MarginPadding { Left = -avatar_size / 2 },
|
||||
@@ -210,8 +216,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
},
|
||||
scoreComponents = new Container
|
||||
{
|
||||
Width = right_panel_width,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = avatar_size / 2 + 4, Right = 20, Vertical = 5 },
|
||||
Shear = -OsuGame.SHEAR,
|
||||
Children = new Drawable[]
|
||||
@@ -223,7 +228,6 @@ namespace osu.Game.Screens.Play.HUD
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.Absolute, 10),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
RowDimensions = new[]
|
||||
@@ -242,7 +246,6 @@ namespace osu.Game.Screens.Play.HUD
|
||||
Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
Empty(),
|
||||
accuracyText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
@@ -252,19 +255,31 @@ namespace osu.Game.Screens.Play.HUD
|
||||
}
|
||||
},
|
||||
},
|
||||
new Container
|
||||
new GridContainer
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new[]
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
scoreText = new OsuSpriteText
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
scoreText = new TruncatingSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Font = OsuFont.Style.Body.With(weight: FontWeight.Regular),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
comboText = new OsuSpriteText
|
||||
{
|
||||
@@ -275,6 +290,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -311,7 +327,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
if (expanded.NewValue)
|
||||
{
|
||||
rightLayer.ResizeWidthTo(right_panel_width, panel_transition_duration, Easing.OutQuint);
|
||||
rightLayer.ResizeWidthTo(computeRightLayerWidth(), panel_transition_duration, Easing.OutQuint);
|
||||
scoreComponents.FadeIn(panel_transition_duration, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
@@ -371,6 +387,34 @@ namespace osu.Game.Screens.Play.HUD
|
||||
scorePanel.BorderColour = ColourInfo.GradientVertical(colours.Blue1.Opacity(0.2f), colours.Blue1);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!drawSizeLayout.IsValid)
|
||||
{
|
||||
if (Expanded.Value)
|
||||
{
|
||||
rightLayer.ClearTransforms(targetMember: nameof(Width));
|
||||
rightLayer.Width = computeRightLayerWidth();
|
||||
}
|
||||
|
||||
drawSizeLayout.Validate();
|
||||
}
|
||||
|
||||
bool showAccuracyAndCombo = rightLayer.Width >= accuracy_combo_width_cutoff;
|
||||
|
||||
accuracyText.Alpha = showAccuracyAndCombo ? 1 : 0;
|
||||
comboText.Alpha = showAccuracyAndCombo ? 1 : 0;
|
||||
|
||||
bool showUsernameAndScore = rightLayer.Width >= username_score_width_cutoff;
|
||||
|
||||
usernameText.Alpha = showUsernameAndScore ? 1 : 0;
|
||||
scoreText.Alpha = showUsernameAndScore ? 1 : 0;
|
||||
}
|
||||
|
||||
private float computeRightLayerWidth() => Math.Max(0, DrawWidth - extended_left_panel_width - avatar_size / 2);
|
||||
|
||||
private partial class ScoreAvatar : CompositeDrawable
|
||||
{
|
||||
private readonly IUser? user;
|
||||
|
||||
166
osu.Game/Screens/Select/PlaySongSelect.cs
Normal file
166
osu.Game/Screens/Select/PlaySongSelect.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
// 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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Utils;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public partial class PlaySongSelect : SongSelect
|
||||
{
|
||||
private OsuScreen? playerLoader;
|
||||
|
||||
[Resolved]
|
||||
private INotificationOverlay? notifications { get; set; }
|
||||
|
||||
public override bool AllowExternalScreenChange => true;
|
||||
|
||||
public override MenuItem[] CreateForwardNavigationMenuItemsForBeatmap(Func<BeatmapInfo> getBeatmap) => new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem(ButtonSystemStrings.Play.ToSentence(), MenuItemType.Highlighted, () => FinaliseSelection(getBeatmap())),
|
||||
new OsuMenuItem(ButtonSystemStrings.Edit.ToSentence(), MenuItemType.Standard, () => Edit(getBeatmap()))
|
||||
};
|
||||
|
||||
protected override UserActivity InitialActivity => new UserActivity.ChoosingBeatmap();
|
||||
|
||||
private PlayBeatmapDetailArea playBeatmapDetailArea = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
BeatmapOptions.AddButton(ButtonSystemStrings.Edit.ToSentence(), @"beatmap", FontAwesome.Solid.PencilAlt, colours.Yellow, () => Edit());
|
||||
|
||||
AddInternal(new SongSelectTouchInputDetector());
|
||||
}
|
||||
|
||||
protected void PresentScore(ScoreInfo score) =>
|
||||
FinaliseSelection(score.BeatmapInfo, score.Ruleset, () => this.Push(new SoloResultsScreen(score)));
|
||||
|
||||
protected override BeatmapDetailArea CreateBeatmapDetailArea()
|
||||
{
|
||||
playBeatmapDetailArea = new PlayBeatmapDetailArea
|
||||
{
|
||||
Leaderboard =
|
||||
{
|
||||
ScoreSelected = PresentScore
|
||||
}
|
||||
};
|
||||
|
||||
return playBeatmapDetailArea;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Enter:
|
||||
case Key.KeypadEnter:
|
||||
// this is a special hard-coded case; we can't rely on OnPressed (of SongSelect) as GlobalActionContainer is
|
||||
// matching with exact modifier consideration (so Ctrl+Enter would be ignored).
|
||||
FinaliseSelection();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
private IReadOnlyList<Mod>? modsAtGameplayStart;
|
||||
|
||||
private ModAutoplay? getAutoplayMod() => Ruleset.Value.CreateInstance().GetAutoplayMod();
|
||||
|
||||
protected override bool OnStart()
|
||||
{
|
||||
if (playerLoader != null) return false;
|
||||
|
||||
modsAtGameplayStart = Mods.Value.Select(m => m.DeepClone()).ToArray();
|
||||
|
||||
// Ctrl+Enter should start map with autoplay enabled.
|
||||
if (GetContainingInputManager()?.CurrentState?.Keyboard.ControlPressed == true)
|
||||
{
|
||||
var autoInstance = getAutoplayMod();
|
||||
|
||||
if (autoInstance == null)
|
||||
{
|
||||
notifications?.Post(new SimpleNotification
|
||||
{
|
||||
Text = NotificationsStrings.NoAutoplayMod
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
var mods = Mods.Value.Append(autoInstance).ToArray();
|
||||
|
||||
if (!ModUtils.CheckCompatibleSet(mods, out var invalid))
|
||||
mods = mods.Except(invalid).Append(autoInstance).ToArray();
|
||||
|
||||
Mods.Value = mods;
|
||||
}
|
||||
|
||||
SampleConfirm?.Play();
|
||||
|
||||
this.Push(playerLoader = new PlayerLoader(createPlayer));
|
||||
return true;
|
||||
|
||||
Player createPlayer()
|
||||
{
|
||||
Player player;
|
||||
|
||||
var replayGeneratingMod = Mods.Value.OfType<ICreateReplayData>().FirstOrDefault();
|
||||
|
||||
if (replayGeneratingMod != null)
|
||||
{
|
||||
player = new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods));
|
||||
}
|
||||
else
|
||||
{
|
||||
player = new SoloPlayer();
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnResuming(ScreenTransitionEvent e)
|
||||
{
|
||||
base.OnResuming(e);
|
||||
revertMods();
|
||||
}
|
||||
|
||||
public override bool OnExiting(ScreenExitEvent e)
|
||||
{
|
||||
if (base.OnExiting(e))
|
||||
return true;
|
||||
|
||||
revertMods();
|
||||
return false;
|
||||
}
|
||||
|
||||
private void revertMods()
|
||||
{
|
||||
if (playerLoader == null) return;
|
||||
|
||||
Mods.Value = modsAtGameplayStart;
|
||||
playerLoader = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,12 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
@@ -20,7 +16,6 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
@@ -55,10 +50,10 @@ namespace osu.Game.Screens.SelectV2
|
||||
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
private IBindable<SongSelect.BeatmapSetLookupResult> onlineLookupResult { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RealmPopulatingOnlineLookupSource onlineLookupSource { get; set; } = null!;
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
@@ -254,6 +249,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
{
|
||||
base.LoadComplete();
|
||||
beatmap.BindValueChanged(_ => updateDisplay());
|
||||
onlineLookupResult.BindValueChanged(_ => updateDisplay());
|
||||
|
||||
apiState = api.State.GetBoundCopy();
|
||||
apiState.BindValueChanged(_ => Scheduler.AddOnce(updateDisplay), true);
|
||||
@@ -283,7 +279,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
// Needs some experimentation on what looks good.
|
||||
|
||||
var beatmapInfo = beatmap.Value.BeatmapInfo;
|
||||
var currentOnlineBeatmap = currentOnlineBeatmapSet?.Beatmaps.SingleOrDefault(b => b.OnlineID == beatmapInfo.OnlineID);
|
||||
var currentOnlineBeatmap = onlineLookupResult.Value?.Result?.Beatmaps.SingleOrDefault(b => b.OnlineID == beatmapInfo.OnlineID);
|
||||
|
||||
if (State.Value == Visibility.Visible && currentOnlineBeatmap != null)
|
||||
{
|
||||
@@ -365,41 +361,12 @@ namespace osu.Game.Screens.SelectV2
|
||||
submitted.Date = beatmapSetInfo.DateSubmitted;
|
||||
ranked.Date = beatmapSetInfo.DateRanked;
|
||||
|
||||
if (currentOnlineBeatmapSet == null || currentOnlineBeatmapSet.OnlineID != beatmapSetInfo.OnlineID)
|
||||
refetchBeatmapSet();
|
||||
|
||||
updateOnlineDisplay();
|
||||
}
|
||||
|
||||
private APIBeatmapSet? currentOnlineBeatmapSet;
|
||||
private CancellationTokenSource? cancellationTokenSource;
|
||||
private Task<APIBeatmapSet?>? currentFetchTask;
|
||||
|
||||
private void refetchBeatmapSet()
|
||||
{
|
||||
var beatmapSetInfo = beatmap.Value.BeatmapSetInfo;
|
||||
|
||||
cancellationTokenSource?.Cancel();
|
||||
currentOnlineBeatmapSet = null;
|
||||
|
||||
if (beatmapSetInfo.OnlineID >= 1)
|
||||
{
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
currentFetchTask = onlineLookupSource.GetBeatmapSetAsync(beatmapSetInfo.OnlineID);
|
||||
currentFetchTask.ContinueWith(t =>
|
||||
{
|
||||
if (t.IsCompletedSuccessfully)
|
||||
currentOnlineBeatmapSet = t.GetResultSafely();
|
||||
if (t.Exception != null)
|
||||
Logger.Log($"Error when fetching online beatmap set: {t.Exception}", LoggingTarget.Network);
|
||||
Scheduler.AddOnce(updateOnlineDisplay);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOnlineDisplay()
|
||||
{
|
||||
if (currentFetchTask?.IsCompleted == false)
|
||||
if (onlineLookupResult.Value?.Status != SongSelect.BeatmapSetLookupStatus.Completed)
|
||||
{
|
||||
genre.Data = null;
|
||||
language.Data = null;
|
||||
@@ -407,7 +374,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentOnlineBeatmapSet == null)
|
||||
if (onlineLookupResult.Value.Result == null)
|
||||
{
|
||||
genre.Data = ("-", null);
|
||||
language.Data = ("-", null);
|
||||
@@ -416,7 +383,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
{
|
||||
var beatmapInfo = beatmap.Value.BeatmapInfo;
|
||||
|
||||
var onlineBeatmapSet = currentOnlineBeatmapSet;
|
||||
var onlineBeatmapSet = onlineLookupResult.Value.Result;
|
||||
var onlineBeatmap = onlineBeatmapSet.Beatmaps.SingleOrDefault(b => b.OnlineID == beatmapInfo.OnlineID);
|
||||
|
||||
genre.Data = (onlineBeatmapSet.Genre.Name, () => songSelect?.Search(onlineBeatmapSet.Genre.Name));
|
||||
@@ -440,6 +407,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
string[] tags = realm.Run(r =>
|
||||
{
|
||||
// need to refetch because `beatmap.Value.BeatmapInfo` is not going to have the latest tags
|
||||
r.Refresh();
|
||||
var refetchedBeatmap = r.Find<BeatmapInfo>(beatmap.Value.BeatmapInfo.ID);
|
||||
return refetchedBeatmap?.Metadata.UserTags.ToArray() ?? [];
|
||||
});
|
||||
|
||||
@@ -8,11 +8,9 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Configuration;
|
||||
@@ -21,7 +19,6 @@ using osu.Game.Extensions;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -43,6 +40,9 @@ namespace osu.Game.Screens.SelectV2
|
||||
[Resolved]
|
||||
private IBindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IBindable<SongSelect.BeatmapSetLookupResult?> onlineLookupResult { get; set; } = null!;
|
||||
|
||||
protected override bool StartHidden => true;
|
||||
|
||||
private ModSettingChangeTracker? settingChangeTracker;
|
||||
@@ -69,16 +69,9 @@ namespace osu.Game.Screens.SelectV2
|
||||
[Resolved]
|
||||
private LocalisationManager localisation { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RealmPopulatingOnlineLookupSource onlineLookupSource { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
|
||||
private APIBeatmapSet? currentOnlineBeatmapSet;
|
||||
private CancellationTokenSource? cancellationTokenSource;
|
||||
private Task<APIBeatmapSet?>? currentFetchTask;
|
||||
|
||||
private FillFlowContainer statisticsFlow = null!;
|
||||
|
||||
public BeatmapTitleWedge()
|
||||
@@ -190,6 +183,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
|
||||
working.BindValueChanged(_ => updateDisplay());
|
||||
ruleset.BindValueChanged(_ => updateDisplay());
|
||||
onlineLookupResult.BindValueChanged(_ => updateDisplay());
|
||||
|
||||
mods.BindValueChanged(m =>
|
||||
{
|
||||
@@ -230,7 +224,6 @@ namespace osu.Game.Screens.SelectV2
|
||||
{
|
||||
var metadata = working.Value.Metadata;
|
||||
var beatmapInfo = working.Value.BeatmapInfo;
|
||||
var beatmapSetInfo = working.Value.BeatmapSetInfo;
|
||||
|
||||
statusPill.Status = beatmapInfo.Status;
|
||||
|
||||
@@ -243,10 +236,6 @@ namespace osu.Game.Screens.SelectV2
|
||||
artistLink.Action = () => songSelect?.Search(artistText.GetPreferred(localisation.CurrentParameters.Value.PreferOriginalScript));
|
||||
|
||||
updateLengthAndBpmStatistics();
|
||||
|
||||
if (currentOnlineBeatmapSet == null || currentOnlineBeatmapSet.OnlineID != beatmapSetInfo.OnlineID)
|
||||
refetchBeatmapSet();
|
||||
|
||||
updateOnlineDisplay();
|
||||
}
|
||||
|
||||
@@ -289,40 +278,18 @@ namespace osu.Game.Screens.SelectV2
|
||||
}, token);
|
||||
}
|
||||
|
||||
private void refetchBeatmapSet()
|
||||
{
|
||||
var beatmapSetInfo = working.Value.BeatmapSetInfo;
|
||||
|
||||
cancellationTokenSource?.Cancel();
|
||||
currentOnlineBeatmapSet = null;
|
||||
|
||||
if (beatmapSetInfo.OnlineID >= 1)
|
||||
{
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
currentFetchTask = onlineLookupSource.GetBeatmapSetAsync(beatmapSetInfo.OnlineID);
|
||||
currentFetchTask.ContinueWith(t =>
|
||||
{
|
||||
if (t.IsCompletedSuccessfully)
|
||||
currentOnlineBeatmapSet = t.GetResultSafely();
|
||||
if (t.Exception != null)
|
||||
Logger.Log($"Error when fetching online beatmap set: {t.Exception}", LoggingTarget.Network);
|
||||
Scheduler.AddOnce(updateOnlineDisplay);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOnlineDisplay()
|
||||
{
|
||||
if (currentFetchTask?.IsCompleted == false)
|
||||
if (onlineLookupResult.Value?.Status != SongSelect.BeatmapSetLookupStatus.Completed)
|
||||
{
|
||||
playCount.Value = null;
|
||||
favouriteButton.SetLoading();
|
||||
}
|
||||
else
|
||||
{
|
||||
var onlineBeatmap = currentOnlineBeatmapSet?.Beatmaps.SingleOrDefault(b => b.OnlineID == working.Value.BeatmapInfo.OnlineID);
|
||||
var onlineBeatmap = onlineLookupResult.Value.Result?.Beatmaps.SingleOrDefault(b => b.OnlineID == working.Value.BeatmapInfo.OnlineID);
|
||||
playCount.Value = new StatisticPlayCount.Data(onlineBeatmap?.PlayCount ?? -1, onlineBeatmap?.UserPlayCount ?? -1);
|
||||
favouriteButton.SetBeatmapSet(currentOnlineBeatmapSet);
|
||||
favouriteButton.SetBeatmapSet(onlineLookupResult.Value.Result);
|
||||
|
||||
// the online fetch may have also updated the beatmap's status.
|
||||
// this needs to be checked against the *local* beatmap model rather than the online one, because it's not known here whether the status change has occurred or not
|
||||
@@ -332,6 +299,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
// which prevents working beatmap refetches caused by changes to the realm model of perceived low importance).
|
||||
var status = realm.Run(r =>
|
||||
{
|
||||
r.Refresh();
|
||||
var refetchedBeatmap = r.Find<BeatmapInfo>(working.Value.BeatmapInfo.ID);
|
||||
return refetchedBeatmap?.Status;
|
||||
});
|
||||
|
||||
@@ -229,6 +229,9 @@ namespace osu.Game.Screens.SelectV2
|
||||
bool hasFavourited = favouriteRequest.Action == BeatmapFavouriteAction.Favourite;
|
||||
beatmapSet.HasFavourited = hasFavourited;
|
||||
beatmapSet.FavouriteCount += hasFavourited ? 1 : -1;
|
||||
|
||||
// if the beatmap set reference changed under the callback, abort visual updates to avoid showing stale data
|
||||
if (onlineBeatmapSet == null || ReferenceEquals(beatmapSet, onlineBeatmapSet))
|
||||
setBeatmapSet(beatmapSet, withHeartAnimation: hasFavourited);
|
||||
};
|
||||
favouriteRequest.Failure += e =>
|
||||
@@ -238,6 +241,9 @@ namespace osu.Game.Screens.SelectV2
|
||||
Text = e.Message,
|
||||
Icon = FontAwesome.Solid.Times,
|
||||
});
|
||||
|
||||
// if the beatmap set reference changed under the callback, abort visual updates to avoid showing stale data
|
||||
if (onlineBeatmapSet == null || ReferenceEquals(beatmapSet, onlineBeatmapSet))
|
||||
setBeatmapSet(beatmapSet, withHeartAnimation: false);
|
||||
};
|
||||
api.Queue(favouriteRequest);
|
||||
|
||||
@@ -5,11 +5,14 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
@@ -34,6 +37,7 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Overlays.Volume;
|
||||
@@ -133,8 +137,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
[Resolved]
|
||||
private IDialogOverlay? dialogOverlay { get; set; }
|
||||
|
||||
[Cached]
|
||||
private RealmPopulatingOnlineLookupSource onlineLookupSource = new RealmPopulatingOnlineLookupSource();
|
||||
private readonly RealmPopulatingOnlineLookupSource onlineLookupSource = new RealmPopulatingOnlineLookupSource();
|
||||
|
||||
private Bindable<bool> configBackgroundBlur = null!;
|
||||
|
||||
@@ -349,6 +352,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
ensurePlayingSelected();
|
||||
updateBackgroundDim();
|
||||
updateWedgeVisibility();
|
||||
fetchOnlineInfo();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -850,7 +854,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
// For simplicity, disable this functionality on mobile.
|
||||
bool isTouchInput = e.CurrentState.Mouse.LastSource is ISourcedFromTouch;
|
||||
|
||||
if (!carousel.AbsoluteScrolling && !isTouchInput && mouseDownPriority)
|
||||
if (!carousel.AbsoluteScrolling && !isTouchInput && mouseDownPriority && revealingBackground == null)
|
||||
{
|
||||
revealingBackground = Scheduler.AddDelayed(() =>
|
||||
{
|
||||
@@ -954,6 +958,74 @@ namespace osu.Game.Screens.SelectV2
|
||||
|
||||
#endregion
|
||||
|
||||
#region Online lookups
|
||||
|
||||
public enum BeatmapSetLookupStatus
|
||||
{
|
||||
InProgress,
|
||||
Completed,
|
||||
}
|
||||
|
||||
public class BeatmapSetLookupResult
|
||||
{
|
||||
public BeatmapSetLookupStatus Status { get; }
|
||||
public APIBeatmapSet? Result { get; }
|
||||
|
||||
private BeatmapSetLookupResult(BeatmapSetLookupStatus status, APIBeatmapSet? result)
|
||||
{
|
||||
Status = status;
|
||||
Result = result;
|
||||
}
|
||||
|
||||
public static BeatmapSetLookupResult InProgress() => new BeatmapSetLookupResult(BeatmapSetLookupStatus.InProgress, null);
|
||||
public static BeatmapSetLookupResult Completed(APIBeatmapSet? beatmapSet) => new BeatmapSetLookupResult(BeatmapSetLookupStatus.Completed, beatmapSet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of the latest online beatmap set lookup.
|
||||
/// Note that this being <see langword="null"/> or <see cref="BeatmapSetLookupResult.InProgress"/> is different from
|
||||
/// being a <see cref="BeatmapSetLookupResult.Completed"/> with a <see cref="BeatmapSetLookupResult.Result"/> of null.
|
||||
/// The former indicates a lookup never occurring or being in progress, while the latter indicates a completed lookup with no result.
|
||||
/// </summary>
|
||||
[Cached(typeof(IBindable<BeatmapSetLookupResult?>))]
|
||||
private readonly Bindable<BeatmapSetLookupResult?> lastLookupResult = new Bindable<BeatmapSetLookupResult?>();
|
||||
|
||||
private CancellationTokenSource? onlineLookupCancellation;
|
||||
private Task<APIBeatmapSet?>? currentOnlineLookup;
|
||||
|
||||
private void fetchOnlineInfo()
|
||||
{
|
||||
var beatmapSetInfo = Beatmap.Value.BeatmapSetInfo;
|
||||
|
||||
if (lastLookupResult.Value?.Result?.OnlineID == beatmapSetInfo.OnlineID)
|
||||
return;
|
||||
|
||||
onlineLookupCancellation?.Cancel();
|
||||
|
||||
if (beatmapSetInfo.OnlineID < 0)
|
||||
{
|
||||
lastLookupResult.Value = BeatmapSetLookupResult.Completed(null);
|
||||
return;
|
||||
}
|
||||
|
||||
lastLookupResult.Value = BeatmapSetLookupResult.InProgress();
|
||||
onlineLookupCancellation = new CancellationTokenSource();
|
||||
currentOnlineLookup = onlineLookupSource.GetBeatmapSetAsync(beatmapSetInfo.OnlineID);
|
||||
currentOnlineLookup.ContinueWith(t =>
|
||||
{
|
||||
if (t.IsCompletedSuccessfully)
|
||||
Schedule(() => lastLookupResult.Value = BeatmapSetLookupResult.Completed(t.GetResultSafely()));
|
||||
|
||||
if (t.Exception != null)
|
||||
{
|
||||
Logger.Log($"Error when fetching online beatmap set: {t.Exception}", LoggingTarget.Network);
|
||||
Schedule(() => lastLookupResult.Value = BeatmapSetLookupResult.Completed(null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of ISongSelect
|
||||
|
||||
void ISongSelect.Search(string query) => filterControl.Search(query);
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="20.1.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2025.808.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2025.819.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2025.821.0" />
|
||||
<PackageReference Include="Sentry" Version="5.1.1" />
|
||||
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
||||
<PackageReference Include="SharpCompress" Version="0.39.0" />
|
||||
|
||||
Reference in New Issue
Block a user