mirror of
https://github.com/ppy/osu.git
synced 2024-12-05 10:23:20 +08:00
Compare commits
205 Commits
4b97e572be
...
fbc3000765
Author | SHA1 | Date | |
---|---|---|---|
|
fbc3000765 | ||
|
544ba25743 | ||
|
f09d8f097a | ||
|
be05f2a1c2 | ||
|
6ff1dec7b2 | ||
|
457957d3b8 | ||
|
2ceb3f6f85 | ||
|
ce4aac4184 | ||
|
e920cfa187 | ||
|
ce8e4120b7 | ||
|
b505ecc7ba | ||
|
b14dde937d | ||
|
6c0ccc5ebe | ||
|
52b8753a12 | ||
|
5b2558cec2 | ||
|
23522b02d8 | ||
|
6afe083ec9 | ||
|
ddac71628d | ||
|
164b809c89 | ||
|
1d610a0f1b | ||
|
f4e155bfa6 | ||
|
1e2e364cd3 | ||
|
e92aa36f47 | ||
|
f56b2b9aef | ||
|
a719693d10 | ||
|
0e1b62ef85 | ||
|
58efed4ebe | ||
|
3cfa455369 | ||
|
5f092811cb | ||
|
5a9127dfc6 | ||
|
110e4fbb30 | ||
|
b71bccc42d | ||
|
b697ddc6db | ||
|
ca32720cbd | ||
|
276c37bcf7 | ||
|
a67f7ea3fb | ||
|
f664e97496 | ||
|
51bcde67aa | ||
|
078d62fe09 | ||
|
a8db35ac45 | ||
|
932afcde01 | ||
|
c93c549b05 | ||
|
9926ffd326 | ||
|
ac2c4e81c7 | ||
|
2e6f43a75d | ||
|
d0e80ce982 | ||
|
6ed21229b7 | ||
|
66093872e8 | ||
|
98a156ae2d | ||
|
1575eed5ba | ||
|
077719903b | ||
|
d218c19799 | ||
|
c26c84ba45 | ||
|
ced8dda1a2 | ||
|
0d491e3159 | ||
|
5d7aafaab3 | ||
|
4314f9c0a9 | ||
|
70eee8882a | ||
|
19e396f878 | ||
|
e0fdcaf523 | ||
|
24c0799680 | ||
|
f792b6de00 | ||
|
4ae3ccfe48 | ||
|
0f73941808 | ||
|
7fdf13911b | ||
|
782ce24ca6 | ||
|
9c707ed341 | ||
|
5ce55e9cb4 | ||
|
c3ac6d7fe5 | ||
|
4fcc76270a | ||
|
5260a401d4 | ||
|
dfbccc2144 | ||
|
aa3d3a6344 | ||
|
a477bb7bfe | ||
|
573aaf6637 | ||
|
9083daf363 | ||
|
16d8b11385 | ||
|
3e1b4f4ac5 | ||
|
80a66085a9 | ||
|
460471e73f | ||
|
df74a177ae | ||
|
a1a63608dd | ||
|
9173a74552 | ||
|
f04862ea74 | ||
|
359cb71dd9 | ||
|
33c2eb1af7 | ||
|
3e373ae85e | ||
|
bd1f978138 | ||
|
c69d36dc96 | ||
|
46d1f00590 | ||
|
cf905d0f5c | ||
|
312336de24 | ||
|
41c309fb72 | ||
|
3ecb3b674d | ||
|
e0199386a3 | ||
|
d3d111de7d | ||
|
943837e3b5 | ||
|
42c68ba43e | ||
|
b76460f100 | ||
|
e3ea38a366 | ||
|
7201bac60d | ||
|
c1416f9920 | ||
|
d150aeef2b | ||
|
dfa21574fd | ||
|
17347563ee | ||
|
f708466a9b | ||
|
d6cf1db0f5 | ||
|
d903d381d5 | ||
|
285959943f | ||
|
9ca17f9b6b | ||
|
b5f8773c0b | ||
|
0a3f3c3210 | ||
|
bbe8f2ec44 | ||
|
ea68d4b33a | ||
|
5668258182 | ||
|
78c01c1b5a | ||
|
c8847e8da8 | ||
|
33d725e889 | ||
|
605fe71f46 | ||
|
a1916d12db | ||
|
82bdd8fbfc | ||
|
876c2e468a | ||
|
cfaf972813 | ||
|
c34827a4ed | ||
|
8611ed31c2 | ||
|
53b390667a | ||
|
888f02e3a6 | ||
|
ae9119eef0 | ||
|
2420793466 | ||
|
6d0d7f3e75 | ||
|
aa1358b2b4 | ||
|
f3155bfc7d | ||
|
631bfadd68 | ||
|
354bc424a3 | ||
|
cab26c70c1 | ||
|
956da0383f | ||
|
8f5d513d46 | ||
|
6b78553559 | ||
|
7a973b0243 | ||
|
2f096f71d3 | ||
|
608bda135a | ||
|
2f45ebeec8 | ||
|
eed02c2ab1 | ||
|
3713bb48b7 | ||
|
62837c7e53 | ||
|
9930922769 | ||
|
c844d65a81 | ||
|
ead7e99c59 | ||
|
c590bef4c3 | ||
|
086a34f5c0 | ||
|
e33e0e16e8 | ||
|
04ed954387 | ||
|
8b68859d9d | ||
|
cfc38df889 | ||
|
69c2c988a1 | ||
|
29757ffdf2 | ||
|
39504c348d | ||
|
e59ac9e7c8 | ||
|
3b2f43012e | ||
|
a76b4418b9 | ||
|
a679f0736e | ||
|
0b52080a52 | ||
|
74daf85e48 | ||
|
4066186b24 | ||
|
7d4062d2ad | ||
|
dcf4674c6c | ||
|
29e7adcd3b | ||
|
b106833663 | ||
|
caf56afba6 | ||
|
1847b679db | ||
|
07609b6267 | ||
|
28f87407f6 | ||
|
4a628287e2 | ||
|
6c8a900dcc | ||
|
0760451f3f | ||
|
979065c421 | ||
|
663b769c71 | ||
|
fdeb8b907e | ||
|
44dd81363a | ||
|
3a57b21c89 | ||
|
2fd495228c | ||
|
701fb565b1 | ||
|
58fe502af4 | ||
|
1d4d806362 | ||
|
d707e29ff7 | ||
|
04527f3c9d | ||
|
c67e2dc301 | ||
|
9fbf2872e1 | ||
|
dd50d6fa6e | ||
|
d373f752d6 | ||
|
7f2a6f6f5a | ||
|
6685c5ab74 | ||
|
3634307d7c | ||
|
c216283bf4 | ||
|
f64a0624a5 | ||
|
1ab86ebd24 | ||
|
a6b7600bf2 | ||
|
011c2e3651 | ||
|
0a4560a03e | ||
|
4339e2dc4a | ||
|
11b3fa8691 | ||
|
bc2b705063 | ||
|
633d85431b | ||
|
3ab60b76df | ||
|
91fb59ee15 |
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@ -114,7 +114,10 @@ jobs:
|
|||||||
dotnet-version: "8.0.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Install .NET workloads
|
- name: Install .NET workloads
|
||||||
run: dotnet workload install android
|
# since windows image 20241113.3.0, not specifying a version here
|
||||||
|
# installs the .NET 7 version of android workload for very unknown reasons.
|
||||||
|
# revisit once we upgrade to .NET 9, it's probably fixed there.
|
||||||
|
run: dotnet workload install android --version (dotnet --version)
|
||||||
|
|
||||||
- name: Compile
|
- name: Compile
|
||||||
run: dotnet build -c Debug osu.Android.slnf
|
run: dotnet build -c Debug osu.Android.slnf
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1118.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1128.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||||
|
@ -15,6 +15,7 @@ using osu.Framework.Threading;
|
|||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Online;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
@ -47,6 +48,9 @@ namespace osu.Desktop
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private MultiplayerClient multiplayerClient { get; set; } = null!;
|
private MultiplayerClient multiplayerClient { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private LocalUserStatisticsProvider statisticsProvider { get; set; } = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuConfigManager config { get; set; } = null!;
|
private OsuConfigManager config { get; set; } = null!;
|
||||||
|
|
||||||
@ -117,7 +121,9 @@ namespace osu.Desktop
|
|||||||
status.BindValueChanged(_ => schedulePresenceUpdate());
|
status.BindValueChanged(_ => schedulePresenceUpdate());
|
||||||
activity.BindValueChanged(_ => schedulePresenceUpdate());
|
activity.BindValueChanged(_ => schedulePresenceUpdate());
|
||||||
privacyMode.BindValueChanged(_ => schedulePresenceUpdate());
|
privacyMode.BindValueChanged(_ => schedulePresenceUpdate());
|
||||||
|
|
||||||
multiplayerClient.RoomUpdated += onRoomUpdated;
|
multiplayerClient.RoomUpdated += onRoomUpdated;
|
||||||
|
statisticsProvider.StatisticsUpdated += onStatisticsUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onReady(object _, ReadyMessage __)
|
private void onReady(object _, ReadyMessage __)
|
||||||
@ -133,6 +139,8 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
private void onRoomUpdated() => schedulePresenceUpdate();
|
private void onRoomUpdated() => schedulePresenceUpdate();
|
||||||
|
|
||||||
|
private void onStatisticsUpdated(UserStatisticsUpdate _) => schedulePresenceUpdate();
|
||||||
|
|
||||||
private ScheduledDelegate? presenceUpdateDelegate;
|
private ScheduledDelegate? presenceUpdateDelegate;
|
||||||
|
|
||||||
private void schedulePresenceUpdate()
|
private void schedulePresenceUpdate()
|
||||||
@ -167,7 +175,7 @@ namespace osu.Desktop
|
|||||||
presence.State = clampLength(activity.Value.GetStatus(hideIdentifiableInformation));
|
presence.State = clampLength(activity.Value.GetStatus(hideIdentifiableInformation));
|
||||||
presence.Details = clampLength(activity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty);
|
presence.Details = clampLength(activity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty);
|
||||||
|
|
||||||
if (getBeatmapID(activity.Value) is int beatmapId && beatmapId > 0)
|
if (activity.Value.GetBeatmapID(hideIdentifiableInformation) is int beatmapId && beatmapId > 0)
|
||||||
{
|
{
|
||||||
presence.Buttons = new[]
|
presence.Buttons = new[]
|
||||||
{
|
{
|
||||||
@ -229,10 +237,8 @@ namespace osu.Desktop
|
|||||||
presence.Assets.LargeImageText = string.Empty;
|
presence.Assets.LargeImageText = string.Empty;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (user.Value.RulesetsStatistics != null && user.Value.RulesetsStatistics.TryGetValue(ruleset.Value.ShortName, out UserStatistics? statistics))
|
var statistics = statisticsProvider.GetStatisticsFor(ruleset.Value);
|
||||||
presence.Assets.LargeImageText = $"{user.Value.Username}" + (statistics.GlobalRank > 0 ? $" (rank #{statistics.GlobalRank:N0})" : string.Empty);
|
presence.Assets.LargeImageText = $"{user.Value.Username}" + (statistics?.GlobalRank > 0 ? $" (rank #{statistics.GlobalRank:N0})" : string.Empty);
|
||||||
else
|
|
||||||
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.GlobalRank > 0 ? $" (rank #{user.Value.Statistics.GlobalRank:N0})" : string.Empty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// small image
|
// small image
|
||||||
@ -327,25 +333,14 @@ namespace osu.Desktop
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int? getBeatmapID(UserActivity activity)
|
|
||||||
{
|
|
||||||
switch (activity)
|
|
||||||
{
|
|
||||||
case UserActivity.InGame game:
|
|
||||||
return game.BeatmapID;
|
|
||||||
|
|
||||||
case UserActivity.EditingBeatmap edit:
|
|
||||||
return edit.BeatmapID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
if (multiplayerClient.IsNotNull())
|
if (multiplayerClient.IsNotNull())
|
||||||
multiplayerClient.RoomUpdated -= onRoomUpdated;
|
multiplayerClient.RoomUpdated -= onRoomUpdated;
|
||||||
|
|
||||||
|
if (statisticsProvider.IsNotNull())
|
||||||
|
statisticsProvider.StatisticsUpdated -= onStatisticsUpdated;
|
||||||
|
|
||||||
client.Dispose();
|
client.Dispose();
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
}
|
}
|
||||||
|
@ -4,28 +4,54 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Benchmarks
|
namespace osu.Game.Benchmarks
|
||||||
{
|
{
|
||||||
public class BenchmarkUnstableRate : BenchmarkTest
|
public class BenchmarkUnstableRate : BenchmarkTest
|
||||||
{
|
{
|
||||||
private List<HitEvent> events = null!;
|
private readonly List<List<HitEvent>> incrementalEventLists = new List<List<HitEvent>>();
|
||||||
|
|
||||||
public override void SetUp()
|
public override void SetUp()
|
||||||
{
|
{
|
||||||
base.SetUp();
|
base.SetUp();
|
||||||
events = new List<HitEvent>();
|
|
||||||
|
|
||||||
for (int i = 0; i < 1000; i++)
|
var events = new List<HitEvent>();
|
||||||
events.Add(new HitEvent(RNG.NextDouble(-200.0, 200.0), RNG.NextDouble(1.0, 2.0), HitResult.Great, new HitObject(), null, null));
|
|
||||||
|
for (int i = 0; i < 2048; i++)
|
||||||
|
{
|
||||||
|
// Ensure the object has hit windows populated.
|
||||||
|
var hitObject = new HitCircle();
|
||||||
|
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
events.Add(new HitEvent(RNG.NextDouble(-200.0, 200.0), RNG.NextDouble(1.0, 2.0), HitResult.Great, hitObject, null, null));
|
||||||
|
|
||||||
|
incrementalEventLists.Add(new List<HitEvent>(events));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public void CalculateUnstableRate()
|
public void CalculateUnstableRate()
|
||||||
{
|
{
|
||||||
_ = events.CalculateUnstableRate();
|
for (int i = 0; i < 2048; i++)
|
||||||
|
{
|
||||||
|
var events = incrementalEventLists[i];
|
||||||
|
_ = events.CalculateUnstableRate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void CalculateUnstableRateUsingIncrementalCalculation()
|
||||||
|
{
|
||||||
|
HitEventExtensions.UnstableRateCalculationResult? last = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < 2048; i++)
|
||||||
|
{
|
||||||
|
var events = incrementalEventLists[i];
|
||||||
|
last = events.CalculateUnstableRate(last);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
|||||||
{
|
{
|
||||||
base.InitialiseDefaults();
|
base.InitialiseDefaults();
|
||||||
|
|
||||||
SetDefault(ManiaRulesetSetting.ScrollSpeed, 8, 1, 40);
|
SetDefault(ManiaRulesetSetting.ScrollSpeed, 8.0, 1.0, 40.0, 0.1);
|
||||||
SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||||
SetDefault(ManiaRulesetSetting.TimingBasedNoteColouring, false);
|
SetDefault(ManiaRulesetSetting.TimingBasedNoteColouring, false);
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
|||||||
|
|
||||||
if (Get<double?>(ManiaRulesetSetting.ScrollTime) is double scrollTime)
|
if (Get<double?>(ManiaRulesetSetting.ScrollTime) is double scrollTime)
|
||||||
{
|
{
|
||||||
SetValue(ManiaRulesetSetting.ScrollSpeed, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / scrollTime));
|
SetValue(ManiaRulesetSetting.ScrollSpeed, Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / scrollTime));
|
||||||
SetValue<double?>(ManiaRulesetSetting.ScrollTime, null);
|
SetValue<double?>(ManiaRulesetSetting.ScrollTime, null);
|
||||||
}
|
}
|
||||||
#pragma warning restore CS0618
|
#pragma warning restore CS0618
|
||||||
@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
|||||||
|
|
||||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||||
{
|
{
|
||||||
new TrackedSetting<int>(ManiaRulesetSetting.ScrollSpeed,
|
new TrackedSetting<double>(ManiaRulesetSetting.ScrollSpeed,
|
||||||
speed => new SettingDescription(
|
speed => new SettingDescription(
|
||||||
rawValue: speed,
|
rawValue: speed,
|
||||||
name: RulesetSettingsStrings.ScrollSpeed,
|
name: RulesetSettingsStrings.ScrollSpeed,
|
||||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
TargetTimeRange = TimelineTimeRange == null || ShowSpeedChanges.Value ? ComputeScrollTime(Config.Get<int>(ManiaRulesetSetting.ScrollSpeed)) : TimelineTimeRange.Value;
|
TargetTimeRange = TimelineTimeRange == null || ShowSpeedChanges.Value ? ComputeScrollTime(Config.Get<double>(ManiaRulesetSetting.ScrollSpeed)) : TimelineTimeRange.Value;
|
||||||
base.Update();
|
base.Update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
|
|||||||
{
|
{
|
||||||
Caption = "Use special (N+1) style",
|
Caption = "Use special (N+1) style",
|
||||||
HintText = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 6K (5+1) or 8K (7+1) configurations.",
|
HintText = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 6K (5+1) or 8K (7+1) configurations.",
|
||||||
Current = { Value = Beatmap.BeatmapInfo.SpecialStyle }
|
Current = { Value = Beatmap.SpecialStyle }
|
||||||
},
|
},
|
||||||
healthDrainSlider = new FormSliderBar<float>
|
healthDrainSlider = new FormSliderBar<float>
|
||||||
{
|
{
|
||||||
@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
|
|||||||
// for now, update these on commit rather than making BeatmapMetadata bindables.
|
// for now, update these on commit rather than making BeatmapMetadata bindables.
|
||||||
// after switching database engines we can reconsider if switching to bindables is a good direction.
|
// after switching database engines we can reconsider if switching to bindables is a good direction.
|
||||||
Beatmap.Difficulty.CircleSize = keyCountSlider.Current.Value;
|
Beatmap.Difficulty.CircleSize = keyCountSlider.Current.Value;
|
||||||
Beatmap.BeatmapInfo.SpecialStyle = specialStyle.Current.Value;
|
Beatmap.SpecialStyle = specialStyle.Current.Value;
|
||||||
Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value;
|
Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value;
|
||||||
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
|
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
|
||||||
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
|
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
|
||||||
|
@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
LabelText = RulesetSettingsStrings.ScrollingDirection,
|
LabelText = RulesetSettingsStrings.ScrollingDirection,
|
||||||
Current = config.GetBindable<ManiaScrollingDirection>(ManiaRulesetSetting.ScrollDirection)
|
Current = config.GetBindable<ManiaScrollingDirection>(ManiaRulesetSetting.ScrollDirection)
|
||||||
},
|
},
|
||||||
new SettingsSlider<int, ManiaScrollSlider>
|
new SettingsSlider<double, ManiaScrollSlider>
|
||||||
{
|
{
|
||||||
LabelText = RulesetSettingsStrings.ScrollSpeed,
|
LabelText = RulesetSettingsStrings.ScrollSpeed,
|
||||||
Current = config.GetBindable<int>(ManiaRulesetSetting.ScrollSpeed),
|
Current = config.GetBindable<double>(ManiaRulesetSetting.ScrollSpeed),
|
||||||
KeyboardStep = 5
|
KeyboardStep = 1
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class ManiaScrollSlider : RoundedSliderBar<int>
|
private partial class ManiaScrollSlider : RoundedSliderBar<double>
|
||||||
{
|
{
|
||||||
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip((int)DrawableManiaRuleset.ComputeScrollTime(Current.Value), Current.Value);
|
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip((int)DrawableManiaRuleset.ComputeScrollTime(Current.Value), Current.Value);
|
||||||
}
|
}
|
||||||
|
@ -164,10 +164,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
|
|
||||||
private Drawable getResult(HitResult result)
|
private Drawable getResult(HitResult result)
|
||||||
{
|
{
|
||||||
if (!hit_result_mapping.ContainsKey(result))
|
if (!hit_result_mapping.TryGetValue(result, out var value))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
string filename = this.GetManiaSkinConfig<string>(hit_result_mapping[result])?.Value
|
string filename = this.GetManiaSkinConfig<string>(value)?.Value
|
||||||
?? default_hit_result_skin_filenames[result];
|
?? default_hit_result_skin_filenames[result];
|
||||||
|
|
||||||
var animation = this.GetAnimation(filename, true, true, frameLength: 1000 / 20d);
|
var animation = this.GetAnimation(filename, true, true, frameLength: 1000 / 20d);
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
||||||
|
|
||||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||||
private readonly BindableInt configScrollSpeed = new BindableInt();
|
private readonly BindableDouble configScrollSpeed = new BindableDouble();
|
||||||
|
|
||||||
private double currentTimeRange;
|
private double currentTimeRange;
|
||||||
protected double TargetTimeRange;
|
protected double TargetTimeRange;
|
||||||
@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="scrollSpeed">The scroll speed.</param>
|
/// <param name="scrollSpeed">The scroll speed.</param>
|
||||||
/// <returns>The scroll time.</returns>
|
/// <returns>The scroll time.</returns>
|
||||||
public static double ComputeScrollTime(int scrollSpeed) => MAX_TIME_RANGE / scrollSpeed;
|
public static double ComputeScrollTime(double scrollSpeed) => MAX_TIME_RANGE / scrollSpeed;
|
||||||
|
|
||||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer();
|
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer();
|
||||||
|
|
||||||
|
@ -231,6 +231,36 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddAssert("slider still has 2 anchors", () => secondSlider.Path.ControlPoints.Count, () => Is.EqualTo(2));
|
AddAssert("slider still has 2 anchors", () => secondSlider.Path.ControlPoints.Count, () => Is.EqualTo(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestControlClickDoesNotDiscardExistingSelectionEvenIfNothingHit()
|
||||||
|
{
|
||||||
|
var firstSlider = new Slider
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Position = new Vector2(0, 0),
|
||||||
|
Path = new SliderPath
|
||||||
|
{
|
||||||
|
ControlPoints =
|
||||||
|
{
|
||||||
|
new PathControlPoint(),
|
||||||
|
new PathControlPoint(new Vector2(100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("add object", () => EditorBeatmap.AddRange([firstSlider]));
|
||||||
|
AddStep("select first slider", () => EditorBeatmap.SelectedHitObjects.AddRange([firstSlider]));
|
||||||
|
|
||||||
|
AddStep("move mouse to middle of playfield", () => InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre));
|
||||||
|
AddStep("control-click left mouse", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
AddAssert("selection preserved", () => EditorBeatmap.SelectedHitObjects.Count, () => Is.EqualTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
private ComposeBlueprintContainer blueprintContainer
|
private ComposeBlueprintContainer blueprintContainer
|
||||||
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();
|
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
private void gridSizeIs(int size)
|
private void gridSizeIs(int size)
|
||||||
=> AddAssert($"grid size is {size}", () => this.ChildrenOfType<RectangularPositionSnapGrid>().Single().Spacing.Value == new Vector2(size)
|
=> AddAssert($"grid size is {size}", () => this.ChildrenOfType<RectangularPositionSnapGrid>().Single().Spacing.Value == new Vector2(size)
|
||||||
&& EditorBeatmap.BeatmapInfo.GridSize == size);
|
&& EditorBeatmap.GridSize == size);
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestGridTypeToggling()
|
public void TestGridTypeToggling()
|
||||||
|
@ -83,10 +83,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BeatmapInfo =
|
StackLeniency = 0,
|
||||||
{
|
|
||||||
StackLeniency = 0,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
ReplayFrames = new List<ReplayFrame>
|
ReplayFrames = new List<ReplayFrame>
|
||||||
{
|
{
|
||||||
|
@ -74,12 +74,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
StackLeniency = 0,
|
|
||||||
Difficulty = new BeatmapDifficulty
|
Difficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
ApproachRate = 8.5f
|
ApproachRate = 8.5f
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
StackLeniency = 0,
|
||||||
ControlPointInfo = controlPointInfo
|
ControlPointInfo = controlPointInfo
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -465,7 +465,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
private void performTest(List<ReplayFrame> frames, Beatmap<OsuHitObject> beatmap)
|
private void performTest(List<ReplayFrame> frames, Beatmap<OsuHitObject> beatmap)
|
||||||
{
|
{
|
||||||
beatmap.BeatmapInfo.Ruleset = new OsuRuleset().RulesetInfo;
|
beatmap.BeatmapInfo.Ruleset = new OsuRuleset().RulesetInfo;
|
||||||
beatmap.BeatmapInfo.StackLeniency = 0;
|
beatmap.StackLeniency = 0;
|
||||||
beatmap.BeatmapInfo.Difficulty = new BeatmapDifficulty
|
beatmap.BeatmapInfo.Difficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
SliderMultiplier = 4,
|
SliderMultiplier = 4,
|
||||||
|
@ -56,13 +56,13 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
h.StackHeight = 0;
|
h.StackHeight = 0;
|
||||||
|
|
||||||
if (beatmap.BeatmapInfo.BeatmapVersion >= 6)
|
if (beatmap.BeatmapInfo.BeatmapVersion >= 6)
|
||||||
applyStacking(beatmap.BeatmapInfo, hitObjects, 0, hitObjects.Count - 1);
|
applyStacking(beatmap, hitObjects, 0, hitObjects.Count - 1);
|
||||||
else
|
else
|
||||||
applyStackingOld(beatmap.BeatmapInfo, hitObjects);
|
applyStackingOld(beatmap, hitObjects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyStacking(BeatmapInfo beatmapInfo, List<OsuHitObject> hitObjects, int startIndex, int endIndex)
|
private static void applyStacking(IBeatmap beatmap, List<OsuHitObject> hitObjects, int startIndex, int endIndex)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex, endIndex);
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex, endIndex);
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(startIndex);
|
ArgumentOutOfRangeException.ThrowIfNegative(startIndex);
|
||||||
@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
double endTime = stackBaseObject.GetEndTime();
|
double endTime = stackBaseObject.GetEndTime();
|
||||||
double stackThreshold = objectN.TimePreempt * beatmapInfo.StackLeniency;
|
double stackThreshold = objectN.TimePreempt * beatmap.StackLeniency;
|
||||||
|
|
||||||
if (objectN.StartTime - endTime > stackThreshold)
|
if (objectN.StartTime - endTime > stackThreshold)
|
||||||
// We are no longer within stacking range of the next object.
|
// We are no longer within stacking range of the next object.
|
||||||
@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
OsuHitObject objectI = hitObjects[i];
|
OsuHitObject objectI = hitObjects[i];
|
||||||
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
||||||
|
|
||||||
double stackThreshold = objectI.TimePreempt * beatmapInfo.StackLeniency;
|
double stackThreshold = objectI.TimePreempt * beatmap.StackLeniency;
|
||||||
|
|
||||||
/* If this object is a hitcircle, then we enter this "special" case.
|
/* If this object is a hitcircle, then we enter this "special" case.
|
||||||
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
||||||
@ -214,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyStackingOld(BeatmapInfo beatmapInfo, List<OsuHitObject> hitObjects)
|
private static void applyStackingOld(IBeatmap beatmap, List<OsuHitObject> hitObjects)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < hitObjects.Count; i++)
|
for (int i = 0; i < hitObjects.Count; i++)
|
||||||
{
|
{
|
||||||
@ -228,7 +228,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
|
|
||||||
for (int j = i + 1; j < hitObjects.Count; j++)
|
for (int j = i + 1; j < hitObjects.Count; j++)
|
||||||
{
|
{
|
||||||
double stackThreshold = hitObjects[i].TimePreempt * beatmapInfo.StackLeniency;
|
double stackThreshold = hitObjects[i].TimePreempt * beatmap.StackLeniency;
|
||||||
|
|
||||||
if (hitObjects[j].StartTime - stackThreshold > startTime)
|
if (hitObjects[j].StartTime - stackThreshold > startTime)
|
||||||
break;
|
break;
|
||||||
|
@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
MinValue = 0f,
|
MinValue = 0f,
|
||||||
MaxValue = OsuPlayfield.BASE_SIZE.X,
|
MaxValue = OsuPlayfield.BASE_SIZE.X,
|
||||||
|
Precision = 0.01f,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -47,6 +48,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
MinValue = 0f,
|
MinValue = 0f,
|
||||||
MaxValue = OsuPlayfield.BASE_SIZE.Y,
|
MaxValue = OsuPlayfield.BASE_SIZE.Y,
|
||||||
|
Precision = 0.01f,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -56,6 +58,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
MinValue = 4f,
|
MinValue = 4f,
|
||||||
MaxValue = 128f,
|
MaxValue = 128f,
|
||||||
|
Precision = 0.01f,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -65,6 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
MinValue = -180f,
|
MinValue = -180f,
|
||||||
MaxValue = 180f,
|
MaxValue = 180f,
|
||||||
|
Precision = 0.01f,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -166,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Spacing.Value = editorBeatmap.BeatmapInfo.GridSize;
|
Spacing.Value = editorBeatmap.GridSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -200,7 +204,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:#,0.##}";
|
spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:#,0.##}";
|
||||||
spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:#,0.##}";
|
spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:#,0.##}";
|
||||||
SpacingVector.Value = new Vector2(spacing.NewValue);
|
SpacingVector.Value = new Vector2(spacing.NewValue);
|
||||||
editorBeatmap.BeatmapInfo.GridSize = (int)spacing.NewValue;
|
editorBeatmap.GridSize = (int)spacing.NewValue;
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
GridLinesRotation.BindValueChanged(rotation =>
|
GridLinesRotation.BindValueChanged(rotation =>
|
||||||
|
@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Setup
|
|||||||
{
|
{
|
||||||
Caption = "Stack Leniency",
|
Caption = "Stack Leniency",
|
||||||
HintText = "In play mode, osu! automatically stacks notes which occur at the same location. Increasing this value means it is more likely to snap notes of further time-distance.",
|
HintText = "In play mode, osu! automatically stacks notes which occur at the same location. Increasing this value means it is more likely to snap notes of further time-distance.",
|
||||||
Current = new BindableFloat(Beatmap.BeatmapInfo.StackLeniency)
|
Current = new BindableFloat(Beatmap.StackLeniency)
|
||||||
{
|
{
|
||||||
Default = 0.7f,
|
Default = 0.7f,
|
||||||
MinValue = 0,
|
MinValue = 0,
|
||||||
@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Setup
|
|||||||
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
|
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
|
||||||
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
|
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
|
||||||
Beatmap.Difficulty.SliderTickRate = tickRateSlider.Current.Value;
|
Beatmap.Difficulty.SliderTickRate = tickRateSlider.Current.Value;
|
||||||
Beatmap.BeatmapInfo.StackLeniency = stackLeniency.Current.Value;
|
Beatmap.StackLeniency = stackLeniency.Current.Value;
|
||||||
|
|
||||||
Beatmap.UpdateAllHitObjects();
|
Beatmap.UpdateAllHitObjects();
|
||||||
Beatmap.SaveState();
|
Beatmap.SaveState();
|
||||||
|
@ -63,18 +63,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Texture = source.GetTexture("spinner-top"),
|
Texture = source.GetTexture("spinner-top"),
|
||||||
},
|
},
|
||||||
fixedMiddle = new Sprite
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Texture = source.GetTexture("spinner-middle"),
|
|
||||||
},
|
|
||||||
spinningMiddle = new Sprite
|
spinningMiddle = new Sprite
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Texture = source.GetTexture("spinner-middle2"),
|
Texture = source.GetTexture("spinner-middle2"),
|
||||||
},
|
},
|
||||||
|
fixedMiddle = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Texture = source.GetTexture("spinner-middle"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -21,14 +21,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
|||||||
return -multiplier * Math.Pow(Math.Cos(denominator * Math.PI * ratio), power);
|
return -multiplier * Math.Pow(Math.Cos(denominator * Math.PI * ratio), power);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gives a bonus for target ratio using a bell-shaped function.
|
|
||||||
/// </summary>
|
|
||||||
private static double bellCurve(double ratio, double targetRatio, double width, double multiplier)
|
|
||||||
{
|
|
||||||
return multiplier * Math.Exp(Math.E * -(Math.Pow(ratio - targetRatio, 2) / Math.Pow(width, 2)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the difficulty of a given ratio using a combination of periodic penalties and bonuses.
|
/// Calculates the difficulty of a given ratio using a combination of periodic penalties and bonuses.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -45,10 +37,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
|||||||
difficulty += terms;
|
difficulty += terms;
|
||||||
|
|
||||||
// Give bonus to near-1 ratios
|
// Give bonus to near-1 ratios
|
||||||
difficulty += bellCurve(ratio, 1, 0.5, 1);
|
difficulty += DifficultyCalculationUtils.BellCurve(ratio, 1, 0.5);
|
||||||
|
|
||||||
// Penalise ratios that are VERY near 1
|
// Penalize ratios that are VERY near 1
|
||||||
difficulty -= bellCurve(ratio, 1, 0.3, 1);
|
difficulty -= DifficultyCalculationUtils.BellCurve(ratio, 1, 0.3);
|
||||||
|
|
||||||
return difficulty / Math.Sqrt(8);
|
return difficulty / Math.Sqrt(8);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Utils;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||||
@ -215,8 +216,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
double staminaPeak = staminaPeaks[i] * 0.0317;
|
double staminaPeak = staminaPeaks[i] * 0.0317;
|
||||||
double readingPeak = readingPeaks[i] * reading_skill_multiplier;
|
double readingPeak = readingPeaks[i] * reading_skill_multiplier;
|
||||||
|
|
||||||
double peak = norm(1.5, colourPeak, staminaPeak);
|
double peak = DifficultyCalculationUtils.Norm(1.5, colourPeak, staminaPeak);
|
||||||
peak = norm(2, peak, rhythmPeak, readingPeak);
|
peak = DifficultyCalculationUtils.Norm(2, peak, rhythmPeak, readingPeak);
|
||||||
|
|
||||||
// Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
|
// Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
|
||||||
// These sections will not contribute to the difficulty.
|
// These sections will not contribute to the difficulty.
|
||||||
@ -263,12 +264,5 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
return 10.43 * Math.Log(sr / 8 + 1);
|
return 10.43 * Math.Log(sr / 8 + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the <i>p</i>-norm of an <i>n</i>-dimensional vector.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="p">The value of <i>p</i> to calculate the norm for.</param>
|
|
||||||
/// <param name="values">The coefficients of the vector.</param>
|
|
||||||
private double norm(double p, params double[] values) => Math.Pow(values.Sum(x => Math.Pow(x, p)), 1 / p);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
foreach (var nested in hitObject.NestedHitObjects)
|
foreach (var nested in hitObject.NestedHitObjects)
|
||||||
simulateHit(nested, ref attributes);
|
simulateHit(nested, ref attributes);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case StrongNestedHitObject:
|
||||||
|
// we never need to deal with these directly.
|
||||||
|
// the only thing strong hits do in terms of scoring is double their object's score increase,
|
||||||
|
// which is already handled at the parent object level via the `strongable.IsStrong` check lower down in this method.
|
||||||
|
// not handling these here can lead to them falsely being counted as combo-increasing when handling strong drum rolls!
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hitObject is DrumRollTick tick)
|
if (hitObject is DrumRollTick tick)
|
||||||
|
@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
double n = totalHits;
|
double n = totalHits;
|
||||||
|
|
||||||
// Proportion of greats + goods hit.
|
// Proportion of greats + goods hit.
|
||||||
double p = Math.Max(0, totalSuccessfulHits - 0.1 * h100) / n;
|
double p = Math.Max(0, totalSuccessfulHits - 0.0005 * countOk) / n;
|
||||||
|
|
||||||
// We can be 99% confident that p is at least this value.
|
// We can be 99% confident that p is at least this value.
|
||||||
double pLowerBound = (n * p + z * z / 2) / (n + z * z) - z / (n + z * z) * Math.Sqrt(n * p * (1 - p) + z * z / 4);
|
double pLowerBound = (n * p + z * z / 2) / (n + z * z) - z / (n + z * z) * Math.Sqrt(n * p * (1 - p) + z * z / 4);
|
||||||
|
@ -80,16 +80,16 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
var metadata = beatmap.Metadata;
|
var metadata = beatmap.Metadata;
|
||||||
|
|
||||||
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile);
|
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile);
|
||||||
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
|
Assert.AreEqual(0, beatmap.AudioLeadIn);
|
||||||
Assert.AreEqual(164471, metadata.PreviewTime);
|
Assert.AreEqual(164471, metadata.PreviewTime);
|
||||||
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
|
Assert.AreEqual(0.7f, beatmap.StackLeniency);
|
||||||
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
|
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
|
||||||
Assert.IsFalse(beatmapInfo.LetterboxInBreaks);
|
Assert.IsFalse(beatmap.LetterboxInBreaks);
|
||||||
Assert.IsFalse(beatmapInfo.SpecialStyle);
|
Assert.IsFalse(beatmap.SpecialStyle);
|
||||||
Assert.IsFalse(beatmapInfo.WidescreenStoryboard);
|
Assert.IsFalse(beatmap.WidescreenStoryboard);
|
||||||
Assert.IsFalse(beatmapInfo.SamplesMatchPlaybackRate);
|
Assert.IsFalse(beatmap.SamplesMatchPlaybackRate);
|
||||||
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
|
Assert.AreEqual(CountdownType.None, beatmap.Countdown);
|
||||||
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
|
Assert.AreEqual(0, beatmap.CountdownOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new LineBufferedReader(resStream))
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
{
|
{
|
||||||
var beatmapInfo = decoder.Decode(stream).BeatmapInfo;
|
var beatmap = decoder.Decode(stream);
|
||||||
|
|
||||||
int[] expectedBookmarks =
|
int[] expectedBookmarks =
|
||||||
{
|
{
|
||||||
@ -109,13 +109,13 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
95901, 106450, 116999, 119637, 130186, 140735, 151285,
|
95901, 106450, 116999, 119637, 130186, 140735, 151285,
|
||||||
161834, 164471, 175020, 185570, 196119, 206669, 209306
|
161834, 164471, 175020, 185570, 196119, 206669, 209306
|
||||||
};
|
};
|
||||||
Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
|
Assert.AreEqual(expectedBookmarks.Length, beatmap.BeatmapInfo.Bookmarks.Length);
|
||||||
for (int i = 0; i < expectedBookmarks.Length; i++)
|
for (int i = 0; i < expectedBookmarks.Length; i++)
|
||||||
Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
|
Assert.AreEqual(expectedBookmarks[i], beatmap.BeatmapInfo.Bookmarks[i]);
|
||||||
Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing);
|
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
|
||||||
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
|
Assert.AreEqual(4, beatmap.BeatmapInfo.BeatDivisor);
|
||||||
Assert.AreEqual(4, beatmapInfo.GridSize);
|
Assert.AreEqual(4, beatmap.GridSize);
|
||||||
Assert.AreEqual(2, beatmapInfo.TimelineZoom);
|
Assert.AreEqual(2, beatmap.TimelineZoom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -993,15 +993,15 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That(decoded.BeatmapInfo.AudioLeadIn, Is.EqualTo(0));
|
Assert.That(decoded.AudioLeadIn, Is.EqualTo(0));
|
||||||
Assert.That(decoded.BeatmapInfo.StackLeniency, Is.EqualTo(0.7f));
|
Assert.That(decoded.StackLeniency, Is.EqualTo(0.7f));
|
||||||
Assert.That(decoded.BeatmapInfo.SpecialStyle, Is.False);
|
Assert.That(decoded.SpecialStyle, Is.False);
|
||||||
Assert.That(decoded.BeatmapInfo.LetterboxInBreaks, Is.False);
|
Assert.That(decoded.LetterboxInBreaks, Is.False);
|
||||||
Assert.That(decoded.BeatmapInfo.WidescreenStoryboard, Is.False);
|
Assert.That(decoded.WidescreenStoryboard, Is.False);
|
||||||
Assert.That(decoded.BeatmapInfo.EpilepsyWarning, Is.False);
|
Assert.That(decoded.EpilepsyWarning, Is.False);
|
||||||
Assert.That(decoded.BeatmapInfo.SamplesMatchPlaybackRate, Is.False);
|
Assert.That(decoded.SamplesMatchPlaybackRate, Is.False);
|
||||||
Assert.That(decoded.BeatmapInfo.Countdown, Is.EqualTo(CountdownType.Normal));
|
Assert.That(decoded.Countdown, Is.EqualTo(CountdownType.None));
|
||||||
Assert.That(decoded.BeatmapInfo.CountdownOffset, Is.EqualTo(0));
|
Assert.That(decoded.CountdownOffset, Is.EqualTo(0));
|
||||||
Assert.That(decoded.BeatmapInfo.Metadata.PreviewTime, Is.EqualTo(-1));
|
Assert.That(decoded.BeatmapInfo.Metadata.PreviewTime, Is.EqualTo(-1));
|
||||||
Assert.That(decoded.BeatmapInfo.Ruleset.OnlineID, Is.EqualTo(0));
|
Assert.That(decoded.BeatmapInfo.Ruleset.OnlineID, Is.EqualTo(0));
|
||||||
});
|
});
|
||||||
|
@ -51,14 +51,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var beatmap = decodeAsJson(normal);
|
var beatmap = decodeAsJson(normal);
|
||||||
var beatmapInfo = beatmap.BeatmapInfo;
|
var beatmapInfo = beatmap.BeatmapInfo;
|
||||||
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
|
Assert.AreEqual(0, beatmap.AudioLeadIn);
|
||||||
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
|
Assert.AreEqual(0.7f, beatmap.StackLeniency);
|
||||||
Assert.AreEqual(false, beatmapInfo.SpecialStyle);
|
Assert.AreEqual(false, beatmap.SpecialStyle);
|
||||||
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
|
Assert.IsTrue(beatmapInfo.Ruleset.OnlineID == 0);
|
||||||
Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
|
Assert.AreEqual(false, beatmap.LetterboxInBreaks);
|
||||||
Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
|
Assert.AreEqual(false, beatmap.WidescreenStoryboard);
|
||||||
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
|
Assert.AreEqual(CountdownType.None, beatmap.Countdown);
|
||||||
Assert.AreEqual(0, beatmapInfo.CountdownOffset);
|
Assert.AreEqual(0, beatmap.CountdownOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -76,10 +76,10 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
|
Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
|
||||||
for (int i = 0; i < expectedBookmarks.Length; i++)
|
for (int i = 0; i < expectedBookmarks.Length; i++)
|
||||||
Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
|
Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
|
||||||
Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing);
|
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
|
||||||
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
|
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
|
||||||
Assert.AreEqual(4, beatmapInfo.GridSize);
|
Assert.AreEqual(4, beatmap.GridSize);
|
||||||
Assert.AreEqual(2, beatmapInfo.TimelineZoom);
|
Assert.AreEqual(2, beatmap.TimelineZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Tests.Database
|
|||||||
Assert.That(lastChanges?.ModifiedIndices, Is.Empty);
|
Assert.That(lastChanges?.ModifiedIndices, Is.Empty);
|
||||||
Assert.That(lastChanges?.NewModifiedIndices, Is.Empty);
|
Assert.That(lastChanges?.NewModifiedIndices, Is.Empty);
|
||||||
|
|
||||||
realm.Write(r => r.All<BeatmapSetInfo>().First().Beatmaps.First().CountdownOffset = 5);
|
realm.Write(r => r.All<BeatmapSetInfo>().First().Beatmaps.First().EditorTimestamp = 5);
|
||||||
realm.Run(r => r.Refresh());
|
realm.Run(r => r.Refresh());
|
||||||
|
|
||||||
Assert.That(collectionChanges, Is.EqualTo(1));
|
Assert.That(collectionChanges, Is.EqualTo(1));
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -64,6 +65,10 @@ namespace osu.Game.Tests
|
|||||||
// Beatmap must be imported before the collection manager is loaded.
|
// Beatmap must be imported before the collection manager is loaded.
|
||||||
if (withBeatmap)
|
if (withBeatmap)
|
||||||
BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely();
|
BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely();
|
||||||
|
|
||||||
|
// the logic for setting the initial ruleset exists in OsuGame rather than OsuGameBase.
|
||||||
|
// the ruleset bindable is not meant to be nullable, so assign any ruleset in here.
|
||||||
|
Ruleset.Value = RulesetStore.AvailableRulesets.First();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,53 @@ namespace osu.Game.Tests.NonVisual.Ranking
|
|||||||
public void TestDistributedHits()
|
public void TestDistributedHits()
|
||||||
{
|
{
|
||||||
var events = Enumerable.Range(-5, 11)
|
var events = Enumerable.Range(-5, 11)
|
||||||
.Select(t => new HitEvent(t - 5, 1.0, HitResult.Great, new HitObject(), null, null));
|
.Select(t => new HitEvent(t - 5, 1.0, HitResult.Great, new HitObject(), null, null))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
var unstableRate = new UnstableRate(events);
|
var unstableRate = new UnstableRate(events);
|
||||||
|
|
||||||
Assert.IsNotNull(unstableRate.Value);
|
Assert.IsNotNull(unstableRate.Value);
|
||||||
Assert.IsTrue(Precision.AlmostEquals(unstableRate.Value.Value, 10 * Math.Sqrt(10)));
|
Assert.AreEqual(unstableRate.Value.Value, 10 * Math.Sqrt(10), Precision.DOUBLE_EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDistributedHitsIncrementalRewind()
|
||||||
|
{
|
||||||
|
var events = Enumerable.Range(-5, 11)
|
||||||
|
.Select(t => new HitEvent(t - 5, 1.0, HitResult.Great, new HitObject(), null, null))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
HitEventExtensions.UnstableRateCalculationResult result = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < events.Count; i++)
|
||||||
|
{
|
||||||
|
result = events.GetRange(0, i + 1)
|
||||||
|
.CalculateUnstableRate(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = events.GetRange(0, 2).CalculateUnstableRate(result);
|
||||||
|
|
||||||
|
Assert.IsNotNull(result!.Result);
|
||||||
|
Assert.AreEqual(5, result.Result, Precision.DOUBLE_EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDistributedHitsIncremental()
|
||||||
|
{
|
||||||
|
var events = Enumerable.Range(-5, 11)
|
||||||
|
.Select(t => new HitEvent(t - 5, 1.0, HitResult.Great, new HitObject(), null, null))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
HitEventExtensions.UnstableRateCalculationResult result = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < events.Count; i++)
|
||||||
|
{
|
||||||
|
result = events.GetRange(0, i + 1)
|
||||||
|
.CalculateUnstableRate(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.IsNotNull(result!.Result);
|
||||||
|
Assert.AreEqual(10 * Math.Sqrt(10), result.Result, Precision.DOUBLE_EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
AddStep("turn countdown off", () => designSection.EnableCountdown.Current.Value = false);
|
AddStep("turn countdown off", () => designSection.EnableCountdown.Current.Value = false);
|
||||||
|
|
||||||
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.None);
|
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.None);
|
||||||
AddUntilStep("other controls hidden", () => !designSection.CountdownSettings.IsPresent);
|
AddUntilStep("other controls hidden", () => !designSection.CountdownSettings.IsPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,12 +65,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
|
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
|
||||||
|
|
||||||
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal);
|
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.Normal);
|
||||||
AddUntilStep("other controls shown", () => designSection.CountdownSettings.IsPresent);
|
AddUntilStep("other controls shown", () => designSection.CountdownSettings.IsPresent);
|
||||||
|
|
||||||
AddStep("change countdown speed", () => designSection.CountdownSpeed.Current.Value = CountdownType.DoubleSpeed);
|
AddStep("change countdown speed", () => designSection.CountdownSpeed.Current.Value = CountdownType.DoubleSpeed);
|
||||||
|
|
||||||
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.DoubleSpeed);
|
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.DoubleSpeed);
|
||||||
AddUntilStep("other controls still shown", () => designSection.CountdownSettings.IsPresent);
|
AddUntilStep("other controls still shown", () => designSection.CountdownSettings.IsPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
|
AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true);
|
||||||
|
|
||||||
AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal);
|
AddAssert("beatmap has correct type", () => editorBeatmap.Countdown == CountdownType.Normal);
|
||||||
|
|
||||||
checkOffsetAfter("1", 1);
|
checkOffsetAfter("1", 1);
|
||||||
checkOffsetAfter(string.Empty, 0);
|
checkOffsetAfter(string.Empty, 0);
|
||||||
@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
||||||
|
|
||||||
AddAssert($"displayed value is {expectedFinalValue}", () => designSection.CountdownOffset.Current.Value == expectedFinalValue.ToString(CultureInfo.InvariantCulture));
|
AddAssert($"displayed value is {expectedFinalValue}", () => designSection.CountdownOffset.Current.Value == expectedFinalValue.ToString(CultureInfo.InvariantCulture));
|
||||||
AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.BeatmapInfo.CountdownOffset == expectedFinalValue);
|
AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.CountdownOffset == expectedFinalValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class TestDesignSection : DesignSection
|
private partial class TestDesignSection : DesignSection
|
||||||
|
@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("Set beat divisor", () => Editor.Dependencies.Get<BindableBeatDivisor>().Value = 16);
|
AddStep("Set beat divisor", () => Editor.Dependencies.Get<BindableBeatDivisor>().Value = 16);
|
||||||
AddStep("Set timeline zoom", () =>
|
AddStep("Set timeline zoom", () =>
|
||||||
{
|
{
|
||||||
originalTimelineZoom = EditorBeatmap.BeatmapInfo.TimelineZoom;
|
originalTimelineZoom = EditorBeatmap.TimelineZoom;
|
||||||
|
|
||||||
var timeline = Editor.ChildrenOfType<Timeline>().Single();
|
var timeline = Editor.ChildrenOfType<Timeline>().Single();
|
||||||
InputManager.MoveMouseTo(timeline);
|
InputManager.MoveMouseTo(timeline);
|
||||||
@ -81,19 +81,19 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddAssert("Ensure timeline zoom changed", () =>
|
AddAssert("Ensure timeline zoom changed", () =>
|
||||||
{
|
{
|
||||||
changedTimelineZoom = EditorBeatmap.BeatmapInfo.TimelineZoom;
|
changedTimelineZoom = EditorBeatmap.TimelineZoom;
|
||||||
return !Precision.AlmostEquals(changedTimelineZoom, originalTimelineZoom);
|
return !Precision.AlmostEquals(changedTimelineZoom, originalTimelineZoom);
|
||||||
});
|
});
|
||||||
|
|
||||||
SaveEditor();
|
SaveEditor();
|
||||||
|
|
||||||
AddAssert("Beatmap has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
|
AddAssert("Beatmap has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
|
||||||
AddAssert("Beatmap has correct timeline zoom", () => EditorBeatmap.BeatmapInfo.TimelineZoom == changedTimelineZoom);
|
AddAssert("Beatmap has correct timeline zoom", () => EditorBeatmap.TimelineZoom == changedTimelineZoom);
|
||||||
|
|
||||||
ReloadEditorToSameBeatmap();
|
ReloadEditorToSameBeatmap();
|
||||||
|
|
||||||
AddAssert("Beatmap still has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
|
AddAssert("Beatmap still has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor == 16);
|
||||||
AddAssert("Beatmap still has correct timeline zoom", () => EditorBeatmap.BeatmapInfo.TimelineZoom == changedTimelineZoom);
|
AddAssert("Beatmap still has correct timeline zoom", () => EditorBeatmap.TimelineZoom == changedTimelineZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -205,7 +205,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
double originalSpacing = 0;
|
double originalSpacing = 0;
|
||||||
|
|
||||||
AddStep("retrieve original spacing", () => originalSpacing = editorBeatmap.BeatmapInfo.DistanceSpacing);
|
AddStep("retrieve original spacing", () => originalSpacing = editorBeatmap.DistanceSpacing);
|
||||||
|
|
||||||
AddStep("hold ctrl", () => InputManager.PressKey(Key.LControl));
|
AddStep("hold ctrl", () => InputManager.PressKey(Key.LControl));
|
||||||
AddStep("hold alt", () => InputManager.PressKey(Key.LAlt));
|
AddStep("hold alt", () => InputManager.PressKey(Key.LAlt));
|
||||||
@ -215,7 +215,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt));
|
AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt));
|
||||||
AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl));
|
AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl));
|
||||||
|
|
||||||
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5);
|
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.DistanceSpacing == originalSpacing + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class EditorBeatmapContainer : PopoverContainer
|
public partial class EditorBeatmapContainer : PopoverContainer
|
||||||
|
@ -527,8 +527,11 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
checkPlacementSampleBank(HitSampleInfo.BANK_NORMAL);
|
checkPlacementSampleBank(HitSampleInfo.BANK_NORMAL);
|
||||||
checkPlacementSampleAdditionBank(HitSampleInfo.BANK_NORMAL);
|
checkPlacementSampleAdditionBank(HitSampleInfo.BANK_NORMAL);
|
||||||
|
|
||||||
void checkPlacementSampleBank(string expected) => AddAssert($"Placement sample is {expected}", () => EditorBeatmap.PlacementObject.Value.Samples.First(s => s.Name == HitSampleInfo.HIT_NORMAL).Bank, () => Is.EqualTo(expected));
|
void checkPlacementSampleBank(string expected) => AddAssert($"Placement sample is {expected}",
|
||||||
void checkPlacementSampleAdditionBank(string expected) => AddAssert($"Placement sample addition is {expected}", () => EditorBeatmap.PlacementObject.Value.Samples.First(s => s.Name != HitSampleInfo.HIT_NORMAL).Bank, () => Is.EqualTo(expected));
|
() => EditorBeatmap.PlacementObject.Value.Samples.First(s => s.Name == HitSampleInfo.HIT_NORMAL).Bank, () => Is.EqualTo(expected));
|
||||||
|
|
||||||
|
void checkPlacementSampleAdditionBank(string expected) => AddAssert($"Placement sample addition is {expected}",
|
||||||
|
() => EditorBeatmap.PlacementObject.Value.Samples.First(s => s.Name != HitSampleInfo.HIT_NORMAL).Bank, () => Is.EqualTo(expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -781,15 +784,39 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
setAdditionBankViaPopover(HitSampleInfo.BANK_SOFT);
|
setAdditionBankViaPopover(HitSampleInfo.BANK_SOFT);
|
||||||
dismissPopover();
|
dismissPopover();
|
||||||
|
|
||||||
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH);
|
assertNoChanges();
|
||||||
hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL);
|
|
||||||
hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_SOFT);
|
|
||||||
|
|
||||||
AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]));
|
AddStep("select first object", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.SelectedHitObjects.Clear();
|
||||||
|
EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]);
|
||||||
|
});
|
||||||
|
assertNoChanges();
|
||||||
|
|
||||||
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH);
|
AddStep("select second object", () =>
|
||||||
hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL);
|
{
|
||||||
hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_SOFT);
|
EditorBeatmap.SelectedHitObjects.Clear();
|
||||||
|
EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[1]);
|
||||||
|
});
|
||||||
|
assertNoChanges();
|
||||||
|
|
||||||
|
AddStep("select first object", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.SelectedHitObjects.Clear();
|
||||||
|
EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]);
|
||||||
|
});
|
||||||
|
assertNoChanges();
|
||||||
|
|
||||||
|
void assertNoChanges()
|
||||||
|
{
|
||||||
|
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH);
|
||||||
|
hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL);
|
||||||
|
hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_SOFT);
|
||||||
|
|
||||||
|
hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL);
|
||||||
|
hitObjectHasSampleNormalBank(1, HitSampleInfo.BANK_SOFT);
|
||||||
|
hitObjectHasSampleAdditionBank(1, HitSampleInfo.BANK_SOFT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () =>
|
private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () =>
|
||||||
@ -883,11 +910,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
return h.Samples.All(o => o.Volume == volume);
|
return h.Samples.All(o => o.Volume == volume);
|
||||||
});
|
});
|
||||||
|
|
||||||
private void hitObjectNodeHasSampleVolume(int objectIndex, int nodeIndex, int volume) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has volume {volume}", () =>
|
private void hitObjectNodeHasSampleVolume(int objectIndex, int nodeIndex, int volume) => AddAssert(
|
||||||
{
|
$"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has volume {volume}", () =>
|
||||||
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
|
{
|
||||||
return h is not null && h.NodeSamples[nodeIndex].All(o => o.Volume == volume);
|
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
|
||||||
});
|
return h is not null && h.NodeSamples[nodeIndex].All(o => o.Volume == volume);
|
||||||
|
});
|
||||||
|
|
||||||
private void setBankViaPopover(string bank) => AddStep($"set bank {bank} via popover", () =>
|
private void setBankViaPopover(string bank) => AddStep($"set bank {bank} via popover", () =>
|
||||||
{
|
{
|
||||||
@ -944,29 +972,33 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
return h.Samples.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
|
return h.Samples.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
|
||||||
});
|
});
|
||||||
|
|
||||||
private void hitObjectNodeHasSamples(int objectIndex, int nodeIndex, params string[] samples) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has samples {string.Join(',', samples)}", () =>
|
private void hitObjectNodeHasSamples(int objectIndex, int nodeIndex, params string[] samples) => AddAssert(
|
||||||
{
|
$"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has samples {string.Join(',', samples)}", () =>
|
||||||
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
|
{
|
||||||
return h is not null && h.NodeSamples[nodeIndex].Select(s => s.Name).SequenceEqual(samples);
|
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
|
||||||
});
|
return h is not null && h.NodeSamples[nodeIndex].Select(s => s.Name).SequenceEqual(samples);
|
||||||
|
});
|
||||||
|
|
||||||
private void hitObjectNodeHasSampleBank(int objectIndex, int nodeIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has bank {bank}", () =>
|
private void hitObjectNodeHasSampleBank(int objectIndex, int nodeIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has bank {bank}",
|
||||||
{
|
() =>
|
||||||
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
|
{
|
||||||
return h is not null && h.NodeSamples[nodeIndex].All(o => o.Bank == bank);
|
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
|
||||||
});
|
return h is not null && h.NodeSamples[nodeIndex].All(o => o.Bank == bank);
|
||||||
|
});
|
||||||
|
|
||||||
private void hitObjectNodeHasSampleNormalBank(int objectIndex, int nodeIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has normal bank {bank}", () =>
|
private void hitObjectNodeHasSampleNormalBank(int objectIndex, int nodeIndex, string bank) => AddAssert(
|
||||||
{
|
$"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has normal bank {bank}", () =>
|
||||||
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
|
{
|
||||||
return h is not null && h.NodeSamples[nodeIndex].Where(o => o.Name == HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
|
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
|
||||||
});
|
return h is not null && h.NodeSamples[nodeIndex].Where(o => o.Name == HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
|
||||||
|
});
|
||||||
|
|
||||||
private void hitObjectNodeHasSampleAdditionBank(int objectIndex, int nodeIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has addition bank {bank}", () =>
|
private void hitObjectNodeHasSampleAdditionBank(int objectIndex, int nodeIndex, string bank) => AddAssert(
|
||||||
{
|
$"{objectIndex.ToOrdinalWords()} object {nodeIndex.ToOrdinalWords()} node has addition bank {bank}", () =>
|
||||||
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
|
{
|
||||||
return h is not null && h.NodeSamples[nodeIndex].Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
|
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex) as IHasRepeats;
|
||||||
});
|
return h is not null && h.NodeSamples[nodeIndex].Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(o => o.Bank == bank);
|
||||||
|
});
|
||||||
|
|
||||||
private void editorTimeIs(double time) => AddAssert($"editor time is {time}", () => Precision.AlmostEquals(EditorClock.CurrentTimeAccurate, time, 1));
|
private void editorTimeIs(double time) => AddAssert($"editor time is {time}", () => Precision.AlmostEquals(EditorClock.CurrentTimeAccurate, time, 1));
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||||
{
|
{
|
||||||
BeatmapInfo = { AudioLeadIn = leadIn }
|
AudioLeadIn = leadIn
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFirstFrameTime(expectedStartTime);
|
checkFirstFrameTime(expectedStartTime);
|
||||||
@ -133,7 +133,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
Text = $"GameplayStartTime: {DrawableRuleset.GameplayStartTime} "
|
Text = $"GameplayStartTime: {DrawableRuleset.GameplayStartTime} "
|
||||||
+ $"FirstHitObjectTime: {FirstHitObjectTime} "
|
+ $"FirstHitObjectTime: {FirstHitObjectTime} "
|
||||||
+ $"LeadInTime: {Beatmap.Value.BeatmapInfo.AudioLeadIn} "
|
+ $"LeadInTime: {Beatmap.Value.Beatmap.AudioLeadIn} "
|
||||||
+ $"FirstFrameClockTime: {FirstFrameClockTime}"
|
+ $"FirstFrameClockTime: {FirstFrameClockTime}"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -136,10 +136,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
var workingBeatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
var workingBeatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
// Add intro time to test quick retry skipping (TestQuickRetry).
|
// Add intro time to test quick retry skipping (TestQuickRetry).
|
||||||
workingBeatmap.BeatmapInfo.AudioLeadIn = 60000;
|
workingBeatmap.Beatmap.AudioLeadIn = 60000;
|
||||||
|
|
||||||
// Set up data for testing disclaimer display.
|
// Set up data for testing disclaimer display.
|
||||||
workingBeatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning ?? false;
|
workingBeatmap.Beatmap.EpilepsyWarning = epilepsyWarning ?? false;
|
||||||
workingBeatmap.BeatmapInfo.Status = onlineStatus ?? BeatmapOnlineStatus.Ranked;
|
workingBeatmap.BeatmapInfo.Status = onlineStatus ?? BeatmapOnlineStatus.Ranked;
|
||||||
|
|
||||||
Beatmap.Value = workingBeatmap;
|
Beatmap.Value = workingBeatmap;
|
||||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||||
{
|
{
|
||||||
BeatmapInfo = { AudioLeadIn = 60000 }
|
AudioLeadIn = 60000
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for skip overlay", () => Player.ChildrenOfType<SkipOverlay>().First().IsButtonVisible);
|
AddUntilStep("wait for skip overlay", () => Player.ChildrenOfType<SkipOverlay>().First().IsButtonVisible);
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("load storyboard with only video", () =>
|
AddStep("load storyboard with only video", () =>
|
||||||
{
|
{
|
||||||
// LegacyStoryboardDecoder doesn't parse WidescreenStoryboard, so it is set manually
|
// LegacyStoryboardDecoder doesn't parse WidescreenStoryboard, so it is set manually
|
||||||
loadStoryboard("storyboard_only_video.osu", s => s.BeatmapInfo.WidescreenStoryboard = false);
|
loadStoryboard("storyboard_only_video.osu", s => s.Beatmap.WidescreenStoryboard = false);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("storyboard is correct width", () => Precision.AlmostEquals(storyboard?.Width ?? 0f, 480 * 16 / 9f));
|
AddAssert("storyboard is correct width", () => Precision.AlmostEquals(storyboard?.Width ?? 0f, 480 * 16 / 9f));
|
||||||
|
@ -10,11 +10,13 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Login;
|
using osu.Game.Overlays.Login;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Tests.Visual.Online;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osu.Game.Users.Drawables;
|
using osu.Game.Users.Drawables;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -31,6 +33,9 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuConfigManager configManager { get; set; } = null!;
|
private OsuConfigManager configManager { get; set; } = null!;
|
||||||
|
|
||||||
|
[Cached(typeof(LocalUserStatisticsProvider))]
|
||||||
|
private readonly TestSceneUserPanel.TestUserStatisticsProvider statisticsProvider = new TestSceneUserPanel.TestUserStatisticsProvider();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
@ -170,6 +175,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
AddStep("enter code", () => loginOverlay.ChildrenOfType<OsuTextBox>().First().Text = "88800088");
|
AddStep("enter code", () => loginOverlay.ChildrenOfType<OsuTextBox>().First().Text = "88800088");
|
||||||
assertAPIState(APIState.Online);
|
assertAPIState(APIState.Online);
|
||||||
|
|
||||||
|
AddStep("feed statistics", () => statisticsProvider.UpdateStatistics(new UserStatistics(), Ruleset.Value));
|
||||||
AddStep("click on flag", () =>
|
AddStep("click on flag", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(loginOverlay.ChildrenOfType<UpdateableFlag>().First());
|
InputManager.MoveMouseTo(loginOverlay.ChildrenOfType<UpdateableFlag>().First());
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
@ -73,5 +75,57 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
((StarFountain)Children[1]).Shoot(-1);
|
((StarFountain)Children[1]).Shoot(-1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestGameplayStarFountainsSetting()
|
||||||
|
{
|
||||||
|
Bindable<bool> starFountainsEnabled = null!;
|
||||||
|
|
||||||
|
AddStep("load configuration", () =>
|
||||||
|
{
|
||||||
|
var config = new OsuConfigManager(LocalStorage);
|
||||||
|
starFountainsEnabled = config.GetBindable<bool>(OsuSetting.StarFountains);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("make fountains", () =>
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new KiaiGameplayFountains.GameplayStarFountain
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
X = 75,
|
||||||
|
},
|
||||||
|
new KiaiGameplayFountains.GameplayStarFountain
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
X = -75,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("enable KiaiStarEffects", () => starFountainsEnabled.Value = true);
|
||||||
|
AddRepeatStep("activate fountains (enabled)", () =>
|
||||||
|
{
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[0]).Shoot(1);
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[1]).Shoot(-1);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
AddStep("disable KiaiStarEffects", () => starFountainsEnabled.Value = false);
|
||||||
|
AddRepeatStep("attempt to activate fountains (disabled)", () =>
|
||||||
|
{
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[0]).Shoot(1);
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[1]).Shoot(-1);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
AddStep("re-enable KiaiStarEffects", () => starFountainsEnabled.Value = true);
|
||||||
|
AddRepeatStep("activate fountains (re-enabled)", () =>
|
||||||
|
{
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[0]).Shoot(1);
|
||||||
|
((KiaiGameplayFountains.GameplayStarFountain)Children[1]).Shoot(-1);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
AddStep("Gain", () =>
|
AddStep("Gain", () =>
|
||||||
{
|
{
|
||||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||||
new ScoreInfo(),
|
new ScoreInfo(),
|
||||||
new UserStatistics
|
new UserStatistics
|
||||||
{
|
{
|
||||||
@ -118,7 +118,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
AddStep("Loss", () =>
|
AddStep("Loss", () =>
|
||||||
{
|
{
|
||||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||||
new ScoreInfo(),
|
new ScoreInfo(),
|
||||||
new UserStatistics
|
new UserStatistics
|
||||||
{
|
{
|
||||||
@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
AddStep("Tiny increase in PP", () =>
|
AddStep("Tiny increase in PP", () =>
|
||||||
{
|
{
|
||||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||||
new ScoreInfo(),
|
new ScoreInfo(),
|
||||||
new UserStatistics
|
new UserStatistics
|
||||||
{
|
{
|
||||||
@ -153,7 +153,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
AddStep("No change 1", () =>
|
AddStep("No change 1", () =>
|
||||||
{
|
{
|
||||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||||
new ScoreInfo(),
|
new ScoreInfo(),
|
||||||
new UserStatistics
|
new UserStatistics
|
||||||
{
|
{
|
||||||
@ -170,7 +170,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
AddStep("Was null", () =>
|
AddStep("Was null", () =>
|
||||||
{
|
{
|
||||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||||
new ScoreInfo(),
|
new ScoreInfo(),
|
||||||
new UserStatistics
|
new UserStatistics
|
||||||
{
|
{
|
||||||
@ -187,7 +187,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
AddStep("Became null", () =>
|
AddStep("Became null", () =>
|
||||||
{
|
{
|
||||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||||
transientUpdateDisplay.LatestUpdate.Value = new UserStatisticsUpdate(
|
transientUpdateDisplay.LatestUpdate.Value = new ScoreBasedUserStatisticsUpdate(
|
||||||
new ScoreInfo(),
|
new ScoreInfo(),
|
||||||
new UserStatistics
|
new UserStatistics
|
||||||
{
|
{
|
||||||
|
@ -406,13 +406,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests spectating with a beatmap that has a high <see cref="BeatmapInfo.AudioLeadIn"/> value.
|
/// Tests spectating with a beatmap that has a high <see cref="IBeatmap.AudioLeadIn"/> value.
|
||||||
///
|
///
|
||||||
/// This test is not intended not to check the correct initial time value, but only to guard against
|
/// This test is not intended not to check the correct initial time value, but only to guard against
|
||||||
/// gameplay potentially getting stuck in a stopped state due to lead in time being present.
|
/// gameplay potentially getting stuck in a stopped state due to lead in time being present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAudioLeadIn() => testLeadIn(b => b.BeatmapInfo.AudioLeadIn = 2000);
|
public void TestAudioLeadIn() => testLeadIn(b => b.Beatmap.AudioLeadIn = 2000);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests spectating with a beatmap that has a storyboard element with a negative start time (i.e. intro storyboard element).
|
/// Tests spectating with a beatmap that has a storyboard element with a negative start time (i.e. intro storyboard element).
|
||||||
|
@ -354,6 +354,23 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
AddAssert("retry count is 1", () => player.RestartCount == 1);
|
AddAssert("retry count is 1", () => player.RestartCount == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLastScoreNullAfterExitingPlayer()
|
||||||
|
{
|
||||||
|
AddUntilStep("wait for last play null", getLastPlay, () => Is.Null);
|
||||||
|
|
||||||
|
var getOriginalPlayer = playToCompletion();
|
||||||
|
|
||||||
|
AddStep("attempt to retry", () => getOriginalPlayer().ChildrenOfType<HotkeyRetryOverlay>().First().Action());
|
||||||
|
AddUntilStep("wait for last play matches player", getLastPlay, () => Is.EqualTo(getOriginalPlayer().Score.ScoreInfo));
|
||||||
|
|
||||||
|
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != getOriginalPlayer() && Game.ScreenStack.CurrentScreen is Player);
|
||||||
|
AddStep("exit player", () => (Game.ScreenStack.CurrentScreen as Player)?.Exit());
|
||||||
|
AddUntilStep("wait for last play null", getLastPlay, () => Is.Null);
|
||||||
|
|
||||||
|
ScoreInfo getLastPlay() => Game.Dependencies.Get<SessionStatics>().Get<ScoreInfo>(Static.LastLocalUserScore);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestRetryImmediatelyAfterCompletion()
|
public void TestRetryImmediatelyAfterCompletion()
|
||||||
{
|
{
|
||||||
|
@ -457,6 +457,61 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
waitForChannel1Visible();
|
waitForChannel1Visible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPublicChannelsSortedByName()
|
||||||
|
{
|
||||||
|
// Intentionally join back to front.
|
||||||
|
AddStep("Show overlay with channel 2", () =>
|
||||||
|
{
|
||||||
|
channelManager.CurrentChannel.Value = channelManager.JoinChannel(testChannel2);
|
||||||
|
chatOverlay.Show();
|
||||||
|
});
|
||||||
|
AddUntilStep("second channel is at top of list", () => getFirstVisiblePublicChannel().Channel == testChannel2);
|
||||||
|
|
||||||
|
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
|
||||||
|
AddUntilStep("first channel is at top of list", () => getFirstVisiblePublicChannel().Channel == testChannel1);
|
||||||
|
|
||||||
|
AddStep("message in channel 2", () =>
|
||||||
|
{
|
||||||
|
testChannel2.AddNewMessages(new Message(1) { Content = "hi!", Sender = new APIUser { Username = "person" } });
|
||||||
|
});
|
||||||
|
AddUntilStep("first channel still at top of list", () => getFirstVisiblePublicChannel().Channel == testChannel1);
|
||||||
|
|
||||||
|
ChannelListItem getFirstVisiblePublicChannel() =>
|
||||||
|
chatOverlay.ChildrenOfType<ChannelList>().Single().PublicChannelGroup.ItemFlow.FlowingChildren.OfType<ChannelListItem>().First(item => item.Channel.Type == ChannelType.Public);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPrivateChannelsSortedByRecent()
|
||||||
|
{
|
||||||
|
Channel pmChannel1 = createPrivateChannel();
|
||||||
|
Channel pmChannel2 = createPrivateChannel();
|
||||||
|
|
||||||
|
joinChannel(pmChannel1);
|
||||||
|
joinChannel(pmChannel2);
|
||||||
|
|
||||||
|
AddStep("Show overlay", () => chatOverlay.Show());
|
||||||
|
|
||||||
|
AddUntilStep("first channel is at top of list", () => getFirstVisiblePMChannel().Channel == pmChannel1);
|
||||||
|
|
||||||
|
AddStep("message in channel 2", () =>
|
||||||
|
{
|
||||||
|
pmChannel2.AddNewMessages(new Message(1) { Content = "hi!", Sender = new APIUser { Username = "person" } });
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for first channel raised to top of list", () => getFirstVisiblePMChannel().Channel == pmChannel2);
|
||||||
|
|
||||||
|
AddStep("message in channel 1", () =>
|
||||||
|
{
|
||||||
|
pmChannel1.AddNewMessages(new Message(1) { Content = "hi!", Sender = new APIUser { Username = "person" } });
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for first channel raised to top of list", () => getFirstVisiblePMChannel().Channel == pmChannel1);
|
||||||
|
|
||||||
|
ChannelListItem getFirstVisiblePMChannel() =>
|
||||||
|
chatOverlay.ChildrenOfType<ChannelList>().Single().PrivateChannelGroup.ItemFlow.FlowingChildren.OfType<ChannelListItem>().First(item => item.Channel.Type == ChannelType.PM);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestKeyboardNewChannel()
|
public void TestKeyboardNewChannel()
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,179 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Online;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Catch;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Taiko;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public partial class TestSceneLocalUserStatisticsProvider : OsuTestScene
|
||||||
|
{
|
||||||
|
private LocalUserStatisticsProvider statisticsProvider = null!;
|
||||||
|
|
||||||
|
private readonly Dictionary<(int userId, string rulesetName), UserStatistics> serverSideStatistics = new Dictionary<(int userId, string rulesetName), UserStatistics>();
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("clear statistics", () => serverSideStatistics.Clear());
|
||||||
|
|
||||||
|
setUser(1000);
|
||||||
|
|
||||||
|
AddStep("setup provider", () =>
|
||||||
|
{
|
||||||
|
OsuTextFlowContainer text;
|
||||||
|
|
||||||
|
((DummyAPIAccess)API).HandleRequest = r =>
|
||||||
|
{
|
||||||
|
switch (r)
|
||||||
|
{
|
||||||
|
case GetUserRequest userRequest:
|
||||||
|
int userId = int.Parse(userRequest.Lookup);
|
||||||
|
string rulesetName = userRequest.Ruleset!.ShortName;
|
||||||
|
var response = new APIUser
|
||||||
|
{
|
||||||
|
Id = userId,
|
||||||
|
Statistics = tryGetStatistics(userId, rulesetName)
|
||||||
|
};
|
||||||
|
|
||||||
|
userRequest.TriggerSuccess(response);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Clear();
|
||||||
|
Add(statisticsProvider = new LocalUserStatisticsProvider());
|
||||||
|
Add(text = new OsuTextFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
});
|
||||||
|
|
||||||
|
statisticsProvider.StatisticsUpdated += update =>
|
||||||
|
{
|
||||||
|
text.Clear();
|
||||||
|
|
||||||
|
foreach (var ruleset in Dependencies.Get<RulesetStore>().AvailableRulesets)
|
||||||
|
{
|
||||||
|
text.AddText(statisticsProvider.GetStatisticsFor(ruleset) is UserStatistics statistics
|
||||||
|
? $"{ruleset.Name} statistics: (total score: {statistics.TotalScore})"
|
||||||
|
: $"{ruleset.Name} statistics: (null)");
|
||||||
|
text.NewLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
text.AddText($"latest update: {update.Ruleset}"
|
||||||
|
+ $" ({(update.OldStatistics?.TotalScore.ToString() ?? "null")} -> {update.NewStatistics.TotalScore})");
|
||||||
|
};
|
||||||
|
|
||||||
|
Ruleset.Value = new OsuRuleset().RulesetInfo;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestInitialStatistics()
|
||||||
|
{
|
||||||
|
AddAssert("osu statistics populated", () => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(4_000_000));
|
||||||
|
AddAssert("taiko statistics populated", () => statisticsProvider.GetStatisticsFor(new TaikoRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(3_000_000));
|
||||||
|
AddAssert("catch statistics populated", () => statisticsProvider.GetStatisticsFor(new CatchRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(2_000_000));
|
||||||
|
AddAssert("mania statistics populated", () => statisticsProvider.GetStatisticsFor(new ManiaRuleset().RulesetInfo)!.TotalScore, () => Is.EqualTo(1_000_000));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUserChanges()
|
||||||
|
{
|
||||||
|
setUser(1001);
|
||||||
|
|
||||||
|
AddStep("update statistics for user 1000", () =>
|
||||||
|
{
|
||||||
|
serverSideStatistics[(1000, "osu")] = new UserStatistics { TotalScore = 5_000_000 };
|
||||||
|
serverSideStatistics[(1000, "taiko")] = new UserStatistics { TotalScore = 6_000_000 };
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("statistics matches user 1001 in osu",
|
||||||
|
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
|
||||||
|
() => Is.EqualTo(4_000_000));
|
||||||
|
|
||||||
|
AddAssert("statistics matches user 1001 in taiko",
|
||||||
|
() => statisticsProvider.GetStatisticsFor(new TaikoRuleset().RulesetInfo)!.TotalScore,
|
||||||
|
() => Is.EqualTo(3_000_000));
|
||||||
|
|
||||||
|
setUser(1000, false);
|
||||||
|
|
||||||
|
AddAssert("statistics matches user 1000 in osu",
|
||||||
|
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
|
||||||
|
() => Is.EqualTo(5_000_000));
|
||||||
|
|
||||||
|
AddAssert("statistics matches user 1000 in taiko",
|
||||||
|
() => statisticsProvider.GetStatisticsFor(new TaikoRuleset().RulesetInfo)!.TotalScore,
|
||||||
|
() => Is.EqualTo(6_000_000));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRefetchStatistics()
|
||||||
|
{
|
||||||
|
UserStatisticsUpdate? update = null;
|
||||||
|
|
||||||
|
setUser(1001);
|
||||||
|
|
||||||
|
AddStep("update statistics server side",
|
||||||
|
() => serverSideStatistics[(1001, "osu")] = new UserStatistics { TotalScore = 9_000_000 });
|
||||||
|
|
||||||
|
AddAssert("statistics match old score",
|
||||||
|
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
|
||||||
|
() => Is.EqualTo(4_000_000));
|
||||||
|
|
||||||
|
AddStep("setup event", () =>
|
||||||
|
{
|
||||||
|
update = null;
|
||||||
|
statisticsProvider.StatisticsUpdated -= onStatisticsUpdated;
|
||||||
|
statisticsProvider.StatisticsUpdated += onStatisticsUpdated;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("request refetch", () => statisticsProvider.RefetchStatistics(new OsuRuleset().RulesetInfo));
|
||||||
|
AddUntilStep("statistics update raised",
|
||||||
|
() => update?.NewStatistics.TotalScore,
|
||||||
|
() => Is.EqualTo(9_000_000));
|
||||||
|
AddAssert("statistics match new score",
|
||||||
|
() => statisticsProvider.GetStatisticsFor(new OsuRuleset().RulesetInfo)!.TotalScore,
|
||||||
|
() => Is.EqualTo(9_000_000));
|
||||||
|
|
||||||
|
void onStatisticsUpdated(UserStatisticsUpdate u) => update = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserStatistics tryGetStatistics(int userId, string rulesetName)
|
||||||
|
=> serverSideStatistics.TryGetValue((userId, rulesetName), out var stats) ? stats : new UserStatistics();
|
||||||
|
|
||||||
|
private void setUser(int userId, bool generateStatistics = true)
|
||||||
|
{
|
||||||
|
AddStep($"set local user to {userId}", () =>
|
||||||
|
{
|
||||||
|
if (generateStatistics)
|
||||||
|
{
|
||||||
|
serverSideStatistics[(userId, "osu")] = new UserStatistics { TotalScore = 4_000_000 };
|
||||||
|
serverSideStatistics[(userId, "taiko")] = new UserStatistics { TotalScore = 3_000_000 };
|
||||||
|
serverSideStatistics[(userId, "fruits")] = new UserStatistics { TotalScore = 2_000_000 };
|
||||||
|
serverSideStatistics[(userId, "mania")] = new UserStatistics { TotalScore = 1_000_000 };
|
||||||
|
}
|
||||||
|
|
||||||
|
((DummyAPIAccess)API).LocalUser.Value = new APIUser { Id = userId };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -11,6 +9,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Online;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -24,17 +23,20 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public partial class TestSceneUserPanel : OsuTestScene
|
public partial class TestSceneUserPanel : OsuTestScene
|
||||||
{
|
{
|
||||||
private readonly Bindable<UserActivity> activity = new Bindable<UserActivity>();
|
private readonly Bindable<UserActivity?> activity = new Bindable<UserActivity?>();
|
||||||
private readonly Bindable<UserStatus?> status = new Bindable<UserStatus?>();
|
private readonly Bindable<UserStatus?> status = new Bindable<UserStatus?>();
|
||||||
|
|
||||||
private UserGridPanel boundPanel1;
|
private UserGridPanel boundPanel1 = null!;
|
||||||
private TestUserListPanel boundPanel2;
|
private TestUserListPanel boundPanel2 = null!;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||||
|
|
||||||
|
[Cached(typeof(LocalUserStatisticsProvider))]
|
||||||
|
private readonly TestUserStatisticsProvider statisticsProvider = new TestUserStatisticsProvider();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IRulesetStore rulesetStore { get; set; }
|
private IRulesetStore rulesetStore { get; set; } = null!;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
@ -42,7 +44,11 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
activity.Value = null;
|
activity.Value = null;
|
||||||
status.Value = null;
|
status.Value = null;
|
||||||
|
|
||||||
Child = new FillFlowContainer
|
Remove(statisticsProvider, false);
|
||||||
|
Clear();
|
||||||
|
Add(statisticsProvider);
|
||||||
|
|
||||||
|
Add(new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -108,7 +114,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Statistics = new UserStatistics { GlobalRank = null, CountryRank = null }
|
Statistics = new UserStatistics { GlobalRank = null, CountryRank = null }
|
||||||
}) { Width = 300 }
|
}) { Width = 300 }
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
boundPanel1.Status.BindTo(status);
|
boundPanel1.Status.BindTo(status);
|
||||||
boundPanel1.Activity.BindTo(activity);
|
boundPanel1.Activity.BindTo(activity);
|
||||||
@ -162,24 +168,21 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
AddStep("update statistics", () =>
|
AddStep("update statistics", () =>
|
||||||
{
|
{
|
||||||
API.UpdateStatistics(new UserStatistics
|
statisticsProvider.UpdateStatistics(new UserStatistics
|
||||||
{
|
{
|
||||||
GlobalRank = RNG.Next(100000),
|
GlobalRank = RNG.Next(100000),
|
||||||
CountryRank = RNG.Next(100000)
|
CountryRank = RNG.Next(100000)
|
||||||
});
|
}, Ruleset.Value);
|
||||||
});
|
});
|
||||||
AddStep("set statistics to something big", () =>
|
AddStep("set statistics to something big", () =>
|
||||||
{
|
{
|
||||||
API.UpdateStatistics(new UserStatistics
|
statisticsProvider.UpdateStatistics(new UserStatistics
|
||||||
{
|
{
|
||||||
GlobalRank = RNG.Next(1_000_000, 100_000_000),
|
GlobalRank = RNG.Next(1_000_000, 100_000_000),
|
||||||
CountryRank = RNG.Next(1_000_000, 100_000_000)
|
CountryRank = RNG.Next(1_000_000, 100_000_000)
|
||||||
});
|
}, Ruleset.Value);
|
||||||
});
|
|
||||||
AddStep("set statistics to empty", () =>
|
|
||||||
{
|
|
||||||
API.UpdateStatistics(new UserStatistics());
|
|
||||||
});
|
});
|
||||||
|
AddStep("set statistics to empty", () => statisticsProvider.UpdateStatistics(new UserStatistics(), Ruleset.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(new BeatmapInfo(), rulesetStore.GetRuleset(rulesetId)!);
|
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(new BeatmapInfo(), rulesetStore.GetRuleset(rulesetId)!);
|
||||||
@ -201,5 +204,11 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
public new TextFlowContainer LastVisitMessage => base.LastVisitMessage;
|
public new TextFlowContainer LastVisitMessage => base.LastVisitMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public partial class TestUserStatisticsProvider : LocalUserStatisticsProvider
|
||||||
|
{
|
||||||
|
public new void UpdateStatistics(UserStatistics newStatistics, RulesetInfo ruleset, Action<UserStatisticsUpdate>? callback = null)
|
||||||
|
=> base.UpdateStatistics(newStatistics, ruleset, callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddSliderStep("weekly best", 0, 250, 1, v => update(s => s.WeeklyStreakBest = v));
|
AddSliderStep("weekly best", 0, 250, 1, v => update(s => s.WeeklyStreakBest = v));
|
||||||
AddSliderStep("top 10%", 0, 999, 0, v => update(s => s.Top10PercentPlacements = v));
|
AddSliderStep("top 10%", 0, 999, 0, v => update(s => s.Top10PercentPlacements = v));
|
||||||
AddSliderStep("top 50%", 0, 999, 0, v => update(s => s.Top50PercentPlacements = v));
|
AddSliderStep("top 50%", 0, 999, 0, v => update(s => s.Top50PercentPlacements = v));
|
||||||
AddSliderStep("playcount", 0, 999, 0, v => update(s => s.PlayCount = v));
|
AddSliderStep("playcount", 0, 1500, 1, v => update(s => s.PlayCount = v));
|
||||||
AddStep("create", () =>
|
AddStep("create", () =>
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
@ -66,8 +66,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestPlayCountRankingTier()
|
public void TestPlayCountRankingTier()
|
||||||
{
|
{
|
||||||
AddAssert("1 before silver", () => DailyChallengeStatsTooltip.TierForPlayCount(30) == RankingTier.Bronze);
|
AddAssert("1 before silver", () => DailyChallengeStatsTooltip.TierForPlayCount(29) == RankingTier.Bronze);
|
||||||
AddAssert("first silver", () => DailyChallengeStatsTooltip.TierForPlayCount(31) == RankingTier.Silver);
|
AddAssert("first silver", () => DailyChallengeStatsTooltip.TierForPlayCount(30) == RankingTier.Silver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -58,6 +59,16 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req is GetUserBeatmapsRequest getUserBeatmapsRequest)
|
||||||
|
{
|
||||||
|
getUserBeatmapsRequest.TriggerSuccess(new List<APIBeatmapSet>
|
||||||
|
{
|
||||||
|
CreateAPIBeatmapSet(),
|
||||||
|
CreateAPIBeatmapSet()
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -25,6 +25,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
protected override bool UseOnlineAPI => false;
|
protected override bool UseOnlineAPI => false;
|
||||||
|
|
||||||
|
private LocalUserStatisticsProvider statisticsProvider = null!;
|
||||||
private UserStatisticsWatcher watcher = null!;
|
private UserStatisticsWatcher watcher = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
@ -107,7 +108,9 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
AddStep("create watcher", () =>
|
AddStep("create watcher", () =>
|
||||||
{
|
{
|
||||||
Child = watcher = new UserStatisticsWatcher();
|
Clear();
|
||||||
|
Add(statisticsProvider = new LocalUserStatisticsProvider());
|
||||||
|
Add(watcher = new UserStatisticsWatcher(statisticsProvider));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +126,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
var ruleset = new OsuRuleset().RulesetInfo;
|
var ruleset = new OsuRuleset().RulesetInfo;
|
||||||
|
|
||||||
UserStatisticsUpdate? update = null;
|
ScoreBasedUserStatisticsUpdate? update = null;
|
||||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||||
|
|
||||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||||
@ -146,7 +149,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
// note ordering - in this test processing completes *before* the registration is added.
|
// note ordering - in this test processing completes *before* the registration is added.
|
||||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||||
|
|
||||||
UserStatisticsUpdate? update = null;
|
ScoreBasedUserStatisticsUpdate? update = null;
|
||||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||||
|
|
||||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
|
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
|
||||||
@ -164,7 +167,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
long scoreId = getScoreId();
|
long scoreId = getScoreId();
|
||||||
var ruleset = new OsuRuleset().RulesetInfo;
|
var ruleset = new OsuRuleset().RulesetInfo;
|
||||||
|
|
||||||
UserStatisticsUpdate? update = null;
|
ScoreBasedUserStatisticsUpdate? update = null;
|
||||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||||
|
|
||||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||||
@ -191,7 +194,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
long scoreId = getScoreId();
|
long scoreId = getScoreId();
|
||||||
var ruleset = new OsuRuleset().RulesetInfo;
|
var ruleset = new OsuRuleset().RulesetInfo;
|
||||||
|
|
||||||
UserStatisticsUpdate? update = null;
|
ScoreBasedUserStatisticsUpdate? update = null;
|
||||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||||
|
|
||||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||||
@ -212,7 +215,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
long scoreId = getScoreId();
|
long scoreId = getScoreId();
|
||||||
var ruleset = new OsuRuleset().RulesetInfo;
|
var ruleset = new OsuRuleset().RulesetInfo;
|
||||||
|
|
||||||
UserStatisticsUpdate? update = null;
|
ScoreBasedUserStatisticsUpdate? update = null;
|
||||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||||
|
|
||||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||||
@ -241,7 +244,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
feignScoreProcessing(userId, ruleset, 6_000_000);
|
feignScoreProcessing(userId, ruleset, 6_000_000);
|
||||||
|
|
||||||
UserStatisticsUpdate? update = null;
|
ScoreBasedUserStatisticsUpdate? update = null;
|
||||||
registerForUpdates(secondScoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
registerForUpdates(secondScoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||||
|
|
||||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, secondScoreId));
|
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, secondScoreId));
|
||||||
@ -259,15 +262,14 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
var ruleset = new OsuRuleset().RulesetInfo;
|
var ruleset = new OsuRuleset().RulesetInfo;
|
||||||
|
|
||||||
UserStatisticsUpdate? update = null;
|
ScoreBasedUserStatisticsUpdate? update = null;
|
||||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||||
|
|
||||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||||
|
|
||||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
|
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
|
||||||
AddUntilStep("update received", () => update != null);
|
AddUntilStep("update received", () => update != null);
|
||||||
AddAssert("local user values are correct", () => dummyAPI.LocalUser.Value.Statistics.TotalScore, () => Is.EqualTo(5_000_000));
|
AddAssert("statistics values are correct", () => statisticsProvider.GetStatisticsFor(ruleset)!.TotalScore, () => Is.EqualTo(5_000_000));
|
||||||
AddAssert("statistics values are correct", () => dummyAPI.Statistics.Value!.TotalScore, () => Is.EqualTo(5_000_000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int nextUserId = 2000;
|
private int nextUserId = 2000;
|
||||||
@ -289,7 +291,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<UserStatisticsUpdate> onUpdateReady) =>
|
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<ScoreBasedUserStatisticsUpdate> onUpdateReady) =>
|
||||||
AddStep("register for updates", () =>
|
AddStep("register for updates", () =>
|
||||||
{
|
{
|
||||||
watcher.RegisterForStatisticsUpdateAfter(
|
watcher.RegisterForStatisticsUpdateAfter(
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -31,7 +32,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
private const int scores_per_result = 10;
|
private const int scores_per_result = 10;
|
||||||
private const int real_user_position = 200;
|
private const int real_user_position = 200;
|
||||||
|
|
||||||
private TestResultsScreen resultsScreen = null!;
|
private ResultsScreen resultsScreen = null!;
|
||||||
|
|
||||||
private int lowestScoreId; // Score ID of the lowest score in the list.
|
private int lowestScoreId; // Score ID of the lowest score in the list.
|
||||||
private int highestScoreId; // Score ID of the highest score in the list.
|
private int highestScoreId; // Score ID of the highest score in the list.
|
||||||
@ -68,11 +69,11 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestShowWithUserScore()
|
public void TestShowUserScore()
|
||||||
{
|
{
|
||||||
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
||||||
|
|
||||||
createResults(() => userScore);
|
createResultsWithScore(() => userScore);
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert("user score selected", () => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.OnlineID == userScore.OnlineID).State == PanelState.Expanded);
|
AddAssert("user score selected", () => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.OnlineID == userScore.OnlineID).State == PanelState.Expanded);
|
||||||
@ -81,11 +82,24 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestShowNullUserScore()
|
public void TestShowUserBest()
|
||||||
|
{
|
||||||
|
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
||||||
|
|
||||||
|
createUserBestResults();
|
||||||
|
waitForDisplay();
|
||||||
|
|
||||||
|
AddAssert("user score selected", () => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.UserID == userScore.UserID).State == PanelState.Expanded);
|
||||||
|
AddAssert($"score panel position is {real_user_position}",
|
||||||
|
() => this.ChildrenOfType<ScorePanel>().Single(p => p.Score.UserID == userScore.UserID).ScorePosition.Value == real_user_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShowNonUserScores()
|
||||||
{
|
{
|
||||||
AddStep("bind user score info handler", () => bindHandler());
|
AddStep("bind user score info handler", () => bindHandler());
|
||||||
|
|
||||||
createResults();
|
createUserBestResults();
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
|
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
|
||||||
@ -96,7 +110,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
{
|
{
|
||||||
AddStep("bind user score info handler", () => bindHandler(true, userScore));
|
AddStep("bind user score info handler", () => bindHandler(true, userScore));
|
||||||
|
|
||||||
createResults(() => userScore);
|
createResultsWithScore(() => userScore);
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert("more than 1 panel displayed", () => this.ChildrenOfType<ScorePanel>().Count() > 1);
|
AddAssert("more than 1 panel displayed", () => this.ChildrenOfType<ScorePanel>().Count() > 1);
|
||||||
@ -104,11 +118,11 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestShowNullUserScoreWithDelay()
|
public void TestShowNonUserScoresWithDelay()
|
||||||
{
|
{
|
||||||
AddStep("bind delayed handler", () => bindHandler(true));
|
AddStep("bind delayed handler", () => bindHandler(true));
|
||||||
|
|
||||||
createResults();
|
createUserBestResults();
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
|
AddAssert("top score selected", () => this.ChildrenOfType<ScorePanel>().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded);
|
||||||
@ -119,7 +133,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
{
|
{
|
||||||
AddStep("bind delayed handler", () => bindHandler(true));
|
AddStep("bind delayed handler", () => bindHandler(true));
|
||||||
|
|
||||||
createResults();
|
createUserBestResults();
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
@ -127,13 +141,16 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
int beforePanelCount = 0;
|
int beforePanelCount = 0;
|
||||||
|
|
||||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||||
AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
||||||
|
|
||||||
|
AddAssert("right loading spinner shown", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
|
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||||
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
|
AddAssert("right loading spinner hidden", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,29 +159,36 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
{
|
{
|
||||||
AddStep("bind delayed handler with scores", () => bindHandler(delayed: true));
|
AddStep("bind delayed handler with scores", () => bindHandler(delayed: true));
|
||||||
|
|
||||||
createResults();
|
createUserBestResults();
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
int beforePanelCount = 0;
|
int beforePanelCount = 0;
|
||||||
|
|
||||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||||
AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
||||||
|
|
||||||
|
AddAssert("right loading spinner shown", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
|
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||||
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
|
AddAssert("right loading spinner hidden", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
|
||||||
|
|
||||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||||
AddStep("bind delayed handler with no scores", () => bindHandler(delayed: true, noScores: true));
|
AddStep("bind delayed handler with no scores", () => bindHandler(delayed: true, noScores: true));
|
||||||
AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
||||||
|
|
||||||
|
AddAssert("right loading spinner shown", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
|
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert("count not increased", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount);
|
AddAssert("count not increased", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount);
|
||||||
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
|
AddAssert("right loading spinner hidden", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
|
||||||
|
|
||||||
AddAssert("no placeholders shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.Zero);
|
AddAssert("no placeholders shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +197,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
{
|
{
|
||||||
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
||||||
|
|
||||||
createResults(() => userScore);
|
createResultsWithScore(() => userScore);
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddStep("bind delayed handler", () => bindHandler(true));
|
AddStep("bind delayed handler", () => bindHandler(true));
|
||||||
@ -183,30 +207,36 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
int beforePanelCount = 0;
|
int beforePanelCount = 0;
|
||||||
|
|
||||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||||
AddStep("scroll to left", () => resultsScreen.ScorePanelList.ChildrenOfType<OsuScrollContainer>().Single().ScrollToStart(false));
|
AddStep("scroll to left", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToStart(false));
|
||||||
|
|
||||||
|
AddAssert("left loading spinner shown", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreLeft).State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddAssert("left loading spinner shown", () => resultsScreen.LeftSpinner.State.Value == Visibility.Visible);
|
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||||
AddAssert("left loading spinner hidden", () => resultsScreen.LeftSpinner.State.Value == Visibility.Hidden);
|
AddAssert("left loading spinner hidden", () =>
|
||||||
|
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreLeft).State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the <see cref="TestUserBestResultsScreen"/> with no scores provided by the API.
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestShowWithNoScores()
|
public void TestShowUserBestWithNoScoresPresent()
|
||||||
{
|
{
|
||||||
AddStep("bind user score info handler", () => bindHandler(noScores: true));
|
AddStep("bind user score info handler", () => bindHandler(noScores: true));
|
||||||
createResults();
|
createUserBestResults();
|
||||||
AddAssert("no scores visible", () => !resultsScreen.ScorePanelList.GetScorePanels().Any());
|
AddAssert("no scores visible", () => !resultsScreen.ChildrenOfType<ScorePanelList>().Single().GetScorePanels().Any());
|
||||||
AddAssert("placeholder shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.EqualTo(1));
|
AddAssert("placeholder shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.EqualTo(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createResults(Func<ScoreInfo>? getScore = null)
|
private void createResultsWithScore(Func<ScoreInfo> getScore)
|
||||||
{
|
{
|
||||||
AddStep("load results", () =>
|
AddStep("load results", () =>
|
||||||
{
|
{
|
||||||
LoadScreen(resultsScreen = new TestResultsScreen(getScore?.Invoke(), 1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
LoadScreen(resultsScreen = new TestScoreResultsScreen(getScore(), 1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||||
{
|
{
|
||||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||||
}));
|
}));
|
||||||
@ -215,14 +245,27 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded);
|
AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createUserBestResults()
|
||||||
|
{
|
||||||
|
AddStep("load results", () =>
|
||||||
|
{
|
||||||
|
LoadScreen(resultsScreen = new TestUserBestResultsScreen(1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||||
|
{
|
||||||
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||||
|
}, 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForDisplay()
|
private void waitForDisplay()
|
||||||
{
|
{
|
||||||
AddUntilStep("wait for scores loaded", () =>
|
AddUntilStep("wait for scores loaded", () =>
|
||||||
requestComplete
|
requestComplete
|
||||||
// request handler may need to fire more than once to get scores.
|
// request handler may need to fire more than once to get scores.
|
||||||
&& totalCount > 0
|
&& totalCount > 0
|
||||||
&& resultsScreen.ScorePanelList.GetScorePanels().Count() == totalCount
|
&& resultsScreen.ChildrenOfType<ScorePanelList>().Single().GetScorePanels().Count() == totalCount
|
||||||
&& resultsScreen.ScorePanelList.AllPanelsVisible);
|
&& resultsScreen.ChildrenOfType<ScorePanelList>().Single().AllPanelsVisible);
|
||||||
AddWaitStep("wait for display", 5);
|
AddWaitStep("wait for display", 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,6 +274,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
// pre-check for requests we should be handling (as they are scheduled below).
|
// pre-check for requests we should be handling (as they are scheduled below).
|
||||||
switch (request)
|
switch (request)
|
||||||
{
|
{
|
||||||
|
case ShowPlaylistScoreRequest:
|
||||||
case ShowPlaylistUserScoreRequest:
|
case ShowPlaylistUserScoreRequest:
|
||||||
case IndexPlaylistScoresRequest:
|
case IndexPlaylistScoresRequest:
|
||||||
break;
|
break;
|
||||||
@ -253,7 +297,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
|
|
||||||
switch (request)
|
switch (request)
|
||||||
{
|
{
|
||||||
case ShowPlaylistUserScoreRequest s:
|
case ShowPlaylistScoreRequest s:
|
||||||
if (userScore == null)
|
if (userScore == null)
|
||||||
triggerFail(s);
|
triggerFail(s);
|
||||||
else
|
else
|
||||||
@ -261,6 +305,14 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ShowPlaylistUserScoreRequest u:
|
||||||
|
if (userScore == null)
|
||||||
|
triggerFail(u);
|
||||||
|
else
|
||||||
|
triggerSuccess(u, createUserResponse(userScore));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case IndexPlaylistScoresRequest i:
|
case IndexPlaylistScoresRequest i:
|
||||||
triggerSuccess(i, createIndexResponse(i, noScores));
|
triggerSuccess(i, createIndexResponse(i, noScores));
|
||||||
break;
|
break;
|
||||||
@ -314,7 +366,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
MaxCombo = userScore.MaxCombo,
|
MaxCombo = userScore.MaxCombo,
|
||||||
User = new APIUser
|
User = new APIUser
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2 + i,
|
||||||
Username = $"peppy{i}",
|
Username = $"peppy{i}",
|
||||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
},
|
},
|
||||||
@ -329,7 +381,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
MaxCombo = userScore.MaxCombo,
|
MaxCombo = userScore.MaxCombo,
|
||||||
User = new APIUser
|
User = new APIUser
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2 + i,
|
||||||
Username = $"peppy{i}",
|
Username = $"peppy{i}",
|
||||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
},
|
},
|
||||||
@ -363,7 +415,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
MaxCombo = 1000,
|
MaxCombo = 1000,
|
||||||
User = new APIUser
|
User = new APIUser
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2 + i,
|
||||||
Username = $"peppy{i}",
|
Username = $"peppy{i}",
|
||||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
},
|
},
|
||||||
@ -410,18 +462,22 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class TestResultsScreen : PlaylistItemUserResultsScreen
|
private partial class TestScoreResultsScreen : PlaylistItemScoreResultsScreen
|
||||||
{
|
{
|
||||||
public new LoadingSpinner LeftSpinner => base.LeftSpinner;
|
public TestScoreResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem)
|
||||||
public new LoadingSpinner CentreSpinner => base.CentreSpinner;
|
|
||||||
public new LoadingSpinner RightSpinner => base.RightSpinner;
|
|
||||||
public new ScorePanelList ScorePanelList => base.ScorePanelList;
|
|
||||||
|
|
||||||
public TestResultsScreen(ScoreInfo? score, int roomId, PlaylistItem playlistItem)
|
|
||||||
: base(score, roomId, playlistItem)
|
: base(score, roomId, playlistItem)
|
||||||
{
|
{
|
||||||
AllowRetry = true;
|
AllowRetry = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private partial class TestUserBestResultsScreen : PlaylistItemUserBestResultsScreen
|
||||||
|
{
|
||||||
|
public TestUserBestResultsScreen(int roomId, PlaylistItem playlistItem, int userId)
|
||||||
|
: base(roomId, playlistItem, userId)
|
||||||
|
{
|
||||||
|
AllowRetry = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,67 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Online.Rooms.RoomStatuses;
|
using osu.Game.Online.Rooms.RoomStatuses;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
using osu.Game.Tests.Visual.OnlinePlay;
|
using osu.Game.Tests.Visual.OnlinePlay;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Playlists
|
namespace osu.Game.Tests.Visual.Playlists
|
||||||
{
|
{
|
||||||
public partial class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene
|
public partial class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene
|
||||||
{
|
{
|
||||||
|
private const double track_length = 10000;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; } = null!;
|
||||||
|
|
||||||
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
|
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
|
||||||
|
|
||||||
|
private BeatmapManager beatmaps = null!;
|
||||||
|
private RulesetStore rulesets = null!;
|
||||||
|
private BeatmapSetInfo? importedSet;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(GameHost host, AudioManager audio)
|
||||||
|
{
|
||||||
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, null, audio, Resources, host, Beatmap.Default));
|
||||||
|
Dependencies.Cache(new ScoreManager(rulesets, () => beatmaps, LocalStorage, Realm, API));
|
||||||
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
|
beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();
|
||||||
|
|
||||||
|
Realm.Write(r =>
|
||||||
|
{
|
||||||
|
foreach (var set in r.All<BeatmapSetInfo>())
|
||||||
|
{
|
||||||
|
foreach (var b in set.Beatmaps)
|
||||||
|
{
|
||||||
|
// These will all have a virtual track length of 1000, see WorkingBeatmap.GetVirtualTrack().
|
||||||
|
b.Length = track_length - 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
importedSet = beatmaps.GetAllUsableBeatmapSets().First();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestStatusUpdateOnEnter()
|
public void TestStatusUpdateOnEnter()
|
||||||
{
|
{
|
||||||
@ -37,5 +84,66 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
|
AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
|
||||||
AddAssert("status is still ended", () => roomScreen.Room.Status, Is.TypeOf<RoomStatusEnded>);
|
AddAssert("status is still ended", () => roomScreen.Room.Status, Is.TypeOf<RoomStatusEnded>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCloseButtonGoesAwayAfterGracePeriod()
|
||||||
|
{
|
||||||
|
Room room = null!;
|
||||||
|
PlaylistsRoomSubScreen roomScreen = null!;
|
||||||
|
|
||||||
|
AddStep("create room", () =>
|
||||||
|
{
|
||||||
|
RoomManager.AddRoom(room = new Room
|
||||||
|
{
|
||||||
|
Name = @"Test Room",
|
||||||
|
Host = api.LocalUser.Value,
|
||||||
|
Category = RoomCategory.Normal,
|
||||||
|
StartDate = DateTimeOffset.Now.AddMinutes(-5).AddSeconds(3),
|
||||||
|
EndDate = DateTimeOffset.Now.AddMinutes(30)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("push screen", () => LoadScreen(roomScreen = new PlaylistsRoomSubScreen(room)));
|
||||||
|
AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
|
||||||
|
AddAssert("close button present", () => roomScreen.ChildrenOfType<DangerousRoundedButton>().Any());
|
||||||
|
AddUntilStep("wait for close button to disappear", () => !roomScreen.ChildrenOfType<DangerousRoundedButton>().Any());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(120_000, true)] // Definitely enough time.
|
||||||
|
[TestCase(45_000, true)] // Enough time.
|
||||||
|
[TestCase(35_000, false)] // Not enough time to complete beatmap after lenience.
|
||||||
|
[TestCase(20_000, false)] // Not enough time.
|
||||||
|
[TestCase(5_000, false)] // Not enough time to complete beatmap before lenience.
|
||||||
|
[TestCase(37_500, true, 2)] // Enough time to complete beatmap after mods are applied.
|
||||||
|
public void TestReadyButtonEnablementPeriod(int offsetMs, bool enabled, double rate = 1)
|
||||||
|
{
|
||||||
|
Room room = null!;
|
||||||
|
PlaylistsRoomSubScreen roomScreen = null!;
|
||||||
|
|
||||||
|
AddStep("create room", () =>
|
||||||
|
{
|
||||||
|
RoomManager.AddRoom(room = new Room
|
||||||
|
{
|
||||||
|
Name = @"Test Room",
|
||||||
|
Host = api.LocalUser.Value,
|
||||||
|
Category = RoomCategory.Normal,
|
||||||
|
StartDate = DateTimeOffset.Now,
|
||||||
|
EndDate = DateTimeOffset.Now.AddMilliseconds(offsetMs),
|
||||||
|
Playlist =
|
||||||
|
[
|
||||||
|
new PlaylistItem(importedSet!.Beatmaps[0])
|
||||||
|
{
|
||||||
|
RequiredMods = rate == 1
|
||||||
|
? []
|
||||||
|
: [new APIMod(new OsuModDoubleTime { SpeedChange = { Value = rate } })]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("push screen", () => LoadScreen(roomScreen = new PlaylistsRoomSubScreen(room)));
|
||||||
|
AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
|
||||||
|
AddUntilStep("ready button enabled", () => roomScreen.ChildrenOfType<PlaylistsReadyButton>().SingleOrDefault()?.Enabled.Value, () => Is.EqualTo(enabled));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,6 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
});
|
});
|
||||||
|
|
||||||
private void displayUpdate(UserStatistics before, UserStatistics after) =>
|
private void displayUpdate(UserStatistics before, UserStatistics after) =>
|
||||||
AddStep("display update", () => overallRanking.StatisticsUpdate.Value = new UserStatisticsUpdate(new ScoreInfo(), before, after));
|
AddStep("display update", () => overallRanking.StatisticsUpdate.Value = new ScoreBasedUserStatisticsUpdate(new ScoreInfo(), before, after));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,12 +91,12 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
UserStatisticsWatcher userStatisticsWatcher = null!;
|
UserStatisticsWatcher userStatisticsWatcher = null!;
|
||||||
ScoreInfo score = null!;
|
ScoreInfo score = null!;
|
||||||
|
|
||||||
AddStep("create user statistics watcher", () => Add(userStatisticsWatcher = new UserStatisticsWatcher()));
|
AddStep("create user statistics watcher", () => Add(userStatisticsWatcher = new UserStatisticsWatcher(new LocalUserStatisticsProvider())));
|
||||||
AddStep("set user statistics update", () =>
|
AddStep("set user statistics update", () =>
|
||||||
{
|
{
|
||||||
score = TestResources.CreateTestScoreInfo();
|
score = TestResources.CreateTestScoreInfo();
|
||||||
score.OnlineID = 1234;
|
score.OnlineID = 1234;
|
||||||
((Bindable<UserStatisticsUpdate>)userStatisticsWatcher.LatestUpdate).Value = new UserStatisticsUpdate(score,
|
((Bindable<ScoreBasedUserStatisticsUpdate>)userStatisticsWatcher.LatestUpdate).Value = new ScoreBasedUserStatisticsUpdate(score,
|
||||||
new UserStatistics
|
new UserStatistics
|
||||||
{
|
{
|
||||||
Level = new UserStatistics.LevelInfo
|
Level = new UserStatistics.LevelInfo
|
||||||
@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
Score = { Value = score },
|
Score = { Value = score },
|
||||||
DisplayedUserStatisticsUpdate =
|
DisplayedUserStatisticsUpdate =
|
||||||
{
|
{
|
||||||
Value = new UserStatisticsUpdate(score, new UserStatistics
|
Value = new ScoreBasedUserStatisticsUpdate(score, new UserStatistics
|
||||||
{
|
{
|
||||||
Level = new UserStatistics.LevelInfo
|
Level = new UserStatistics.LevelInfo
|
||||||
{
|
{
|
||||||
|
@ -8,14 +8,14 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Catch;
|
using osu.Game.Rulesets.Catch;
|
||||||
using osu.Game.Rulesets.Mania;
|
using osu.Game.Rulesets.Mania;
|
||||||
@ -28,25 +28,31 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
public partial class TestSceneBeatmapRecommendations : OsuGameTestScene
|
public partial class TestSceneBeatmapRecommendations : OsuGameTestScene
|
||||||
{
|
{
|
||||||
[Resolved]
|
|
||||||
private IRulesetStore rulesetStore { get; set; }
|
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
{
|
{
|
||||||
AddStep("populate ruleset statistics", () =>
|
AddStep("populate ruleset statistics", () =>
|
||||||
{
|
{
|
||||||
Dictionary<string, UserStatistics> rulesetStatistics = new Dictionary<string, UserStatistics>();
|
((DummyAPIAccess)API).HandleRequest = r =>
|
||||||
|
|
||||||
rulesetStore.AvailableRulesets.Where(ruleset => ruleset.IsLegacyRuleset()).ForEach(rulesetInfo =>
|
|
||||||
{
|
{
|
||||||
rulesetStatistics[rulesetInfo.ShortName] = new UserStatistics
|
switch (r)
|
||||||
{
|
{
|
||||||
PP = getNecessaryPP(rulesetInfo.OnlineID)
|
case GetUserRequest userRequest:
|
||||||
};
|
userRequest.TriggerSuccess(new APIUser
|
||||||
});
|
{
|
||||||
|
Id = 99,
|
||||||
|
Statistics = new UserStatistics
|
||||||
|
{
|
||||||
|
PP = getNecessaryPP(userRequest.Ruleset?.OnlineID ?? 0)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
API.LocalUser.Value.RulesetsStatistics = rulesetStatistics;
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
decimal getNecessaryPP(int? rulesetID)
|
decimal getNecessaryPP(int? rulesetID)
|
||||||
|
@ -115,6 +115,30 @@ namespace osu.Game.Beatmaps
|
|||||||
return mostCommon.beatLength;
|
return mostCommon.beatLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double AudioLeadIn { get; set; }
|
||||||
|
|
||||||
|
public float StackLeniency { get; set; } = 0.7f;
|
||||||
|
|
||||||
|
public bool SpecialStyle { get; set; }
|
||||||
|
|
||||||
|
public bool LetterboxInBreaks { get; set; }
|
||||||
|
|
||||||
|
public bool WidescreenStoryboard { get; set; } = true;
|
||||||
|
|
||||||
|
public bool EpilepsyWarning { get; set; }
|
||||||
|
|
||||||
|
public bool SamplesMatchPlaybackRate { get; set; }
|
||||||
|
|
||||||
|
public double DistanceSpacing { get; set; } = 1.0;
|
||||||
|
|
||||||
|
public int GridSize { get; set; }
|
||||||
|
|
||||||
|
public double TimelineZoom { get; set; } = 1.0;
|
||||||
|
|
||||||
|
public CountdownType Countdown { get; set; } = CountdownType.None;
|
||||||
|
|
||||||
|
public int CountdownOffset { get; set; }
|
||||||
|
|
||||||
IBeatmap IBeatmap.Clone() => Clone();
|
IBeatmap IBeatmap.Clone() => Clone();
|
||||||
|
|
||||||
public Beatmap<T> Clone() => (Beatmap<T>)MemberwiseClone();
|
public Beatmap<T> Clone() => (Beatmap<T>)MemberwiseClone();
|
||||||
|
@ -73,6 +73,18 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmap.HitObjects = convertHitObjects(original.HitObjects, original, cancellationToken).OrderBy(s => s.StartTime).ToList();
|
beatmap.HitObjects = convertHitObjects(original.HitObjects, original, cancellationToken).OrderBy(s => s.StartTime).ToList();
|
||||||
beatmap.Breaks = original.Breaks;
|
beatmap.Breaks = original.Breaks;
|
||||||
beatmap.UnhandledEventLines = original.UnhandledEventLines;
|
beatmap.UnhandledEventLines = original.UnhandledEventLines;
|
||||||
|
beatmap.AudioLeadIn = original.AudioLeadIn;
|
||||||
|
beatmap.StackLeniency = original.StackLeniency;
|
||||||
|
beatmap.SpecialStyle = original.SpecialStyle;
|
||||||
|
beatmap.LetterboxInBreaks = original.LetterboxInBreaks;
|
||||||
|
beatmap.WidescreenStoryboard = original.WidescreenStoryboard;
|
||||||
|
beatmap.EpilepsyWarning = original.EpilepsyWarning;
|
||||||
|
beatmap.SamplesMatchPlaybackRate = original.SamplesMatchPlaybackRate;
|
||||||
|
beatmap.DistanceSpacing = original.DistanceSpacing;
|
||||||
|
beatmap.GridSize = original.GridSize;
|
||||||
|
beatmap.TimelineZoom = original.TimelineZoom;
|
||||||
|
beatmap.Countdown = original.Countdown;
|
||||||
|
beatmap.CountdownOffset = original.CountdownOffset;
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
|
@ -428,17 +428,7 @@ namespace osu.Game.Beatmaps
|
|||||||
Hash = hash,
|
Hash = hash,
|
||||||
DifficultyName = decodedInfo.DifficultyName,
|
DifficultyName = decodedInfo.DifficultyName,
|
||||||
OnlineID = decodedInfo.OnlineID,
|
OnlineID = decodedInfo.OnlineID,
|
||||||
AudioLeadIn = decodedInfo.AudioLeadIn,
|
|
||||||
StackLeniency = decodedInfo.StackLeniency,
|
|
||||||
SpecialStyle = decodedInfo.SpecialStyle,
|
|
||||||
LetterboxInBreaks = decodedInfo.LetterboxInBreaks,
|
|
||||||
WidescreenStoryboard = decodedInfo.WidescreenStoryboard,
|
|
||||||
EpilepsyWarning = decodedInfo.EpilepsyWarning,
|
|
||||||
SamplesMatchPlaybackRate = decodedInfo.SamplesMatchPlaybackRate,
|
|
||||||
DistanceSpacing = decodedInfo.DistanceSpacing,
|
|
||||||
BeatDivisor = decodedInfo.BeatDivisor,
|
BeatDivisor = decodedInfo.BeatDivisor,
|
||||||
GridSize = decodedInfo.GridSize,
|
|
||||||
TimelineZoom = decodedInfo.TimelineZoom,
|
|
||||||
MD5Hash = memoryStream.ComputeMD5Hash(),
|
MD5Hash = memoryStream.ComputeMD5Hash(),
|
||||||
EndTimeObjectCount = decoded.HitObjects.Count(h => h is IHasDuration),
|
EndTimeObjectCount = decoded.HitObjects.Count(h => h is IHasDuration),
|
||||||
TotalObjectCount = decoded.HitObjects.Count
|
TotalObjectCount = decoded.HitObjects.Count
|
||||||
|
@ -6,14 +6,12 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Collections;
|
using osu.Game.Collections;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Models;
|
using osu.Game.Models;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays.BeatmapSet.Scores;
|
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using Realms;
|
using Realms;
|
||||||
|
|
||||||
@ -136,60 +134,18 @@ namespace osu.Game.Beatmaps
|
|||||||
Status = BeatmapOnlineStatus.None;
|
Status = BeatmapOnlineStatus.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Properties we may not want persisted (but also maybe no harm?)
|
|
||||||
|
|
||||||
public double AudioLeadIn { get; set; }
|
|
||||||
|
|
||||||
public float StackLeniency { get; set; } = 0.7f;
|
|
||||||
|
|
||||||
public bool SpecialStyle { get; set; }
|
|
||||||
|
|
||||||
public bool LetterboxInBreaks { get; set; }
|
|
||||||
|
|
||||||
public bool WidescreenStoryboard { get; set; } = true;
|
|
||||||
|
|
||||||
public bool EpilepsyWarning { get; set; }
|
|
||||||
|
|
||||||
public bool SamplesMatchPlaybackRate { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time at which this beatmap was last played by the local user.
|
/// The time at which this beatmap was last played by the local user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTimeOffset? LastPlayed { get; set; }
|
public DateTimeOffset? LastPlayed { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The ratio of distance travelled per time unit.
|
|
||||||
/// Generally used to decouple the spacing between hit objects from the enforced "velocity" of the beatmap (see <see cref="DifficultyControlPoint.SliderVelocity"/>).
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The most common method of understanding is that at a default value of 1.0, the time-to-distance ratio will match the slider velocity of the beatmap
|
|
||||||
/// at the current point in time. Increasing this value will make hit objects more spaced apart when compared to the cursor movement required to track a slider.
|
|
||||||
///
|
|
||||||
/// This is only a hint property, used by the editor in <see cref="IDistanceSnapProvider"/> implementations. It does not directly affect the beatmap or gameplay.
|
|
||||||
/// </remarks>
|
|
||||||
public double DistanceSpacing { get; set; } = 1.0;
|
|
||||||
|
|
||||||
public int BeatDivisor { get; set; } = 4;
|
public int BeatDivisor { get; set; } = 4;
|
||||||
|
|
||||||
public int GridSize { get; set; }
|
|
||||||
|
|
||||||
public double TimelineZoom { get; set; } = 1.0;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time in milliseconds when last exiting the editor with this beatmap loaded.
|
/// The time in milliseconds when last exiting the editor with this beatmap loaded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double? EditorTimestamp { get; set; }
|
public double? EditorTimestamp { get; set; }
|
||||||
|
|
||||||
[Ignored]
|
|
||||||
public CountdownType Countdown { get; set; } = CountdownType.Normal;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of beats to move the countdown backwards (compared to its default location).
|
|
||||||
/// </summary>
|
|
||||||
public int CountdownOffset { get; set; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public bool Equals(BeatmapInfo? other)
|
public bool Equals(BeatmapInfo? other)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(this, other)) return true;
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
@ -408,7 +408,7 @@ namespace osu.Game.Beatmaps
|
|||||||
// user requested abort
|
// user requested abort
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var video = b.Files.FirstOrDefault(f => OsuGameBase.VIDEO_EXTENSIONS.Any(ex => f.Filename.EndsWith(ex, StringComparison.OrdinalIgnoreCase)));
|
var video = b.Files.FirstOrDefault(f => SupportedExtensions.VIDEO_EXTENSIONS.Any(ex => f.Filename.EndsWith(ex, StringComparison.OrdinalIgnoreCase)));
|
||||||
|
|
||||||
if (video != null)
|
if (video != null)
|
||||||
{
|
{
|
||||||
@ -559,7 +559,11 @@ namespace osu.Game.Beatmaps
|
|||||||
// If we seem to be missing files, now is a good time to re-fetch.
|
// If we seem to be missing files, now is a good time to re-fetch.
|
||||||
bool missingFiles = beatmapInfo.BeatmapSet?.Files.Count == 0;
|
bool missingFiles = beatmapInfo.BeatmapSet?.Files.Count == 0;
|
||||||
|
|
||||||
if (refetch || beatmapInfo.IsManaged || missingFiles)
|
if (beatmapInfo.IsManaged)
|
||||||
|
{
|
||||||
|
beatmapInfo = beatmapInfo.Detach();
|
||||||
|
}
|
||||||
|
else if (refetch || missingFiles)
|
||||||
{
|
{
|
||||||
Guid id = beatmapInfo.ID;
|
Guid id = beatmapInfo.ID;
|
||||||
beatmapInfo = Realm.Run(r => r.Find<BeatmapInfo>(id)?.Detach()) ?? beatmapInfo;
|
beatmapInfo = Realm.Run(r => r.Find<BeatmapInfo>(id)?.Detach()) ?? beatmapInfo;
|
||||||
|
@ -9,9 +9,11 @@ using System.Linq;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
@ -21,18 +23,63 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class DifficultyRecommender : Component
|
public partial class DifficultyRecommender : Component
|
||||||
{
|
{
|
||||||
[Resolved]
|
private readonly LocalUserStatisticsProvider statisticsProvider;
|
||||||
private IAPIProvider api { get; set; }
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private Bindable<RulesetInfo> ruleset { get; set; }
|
private Bindable<RulesetInfo> gameRuleset { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesets { get; set; } = null!;
|
||||||
|
|
||||||
private readonly Dictionary<string, double> recommendedDifficultyMapping = new Dictionary<string, double>();
|
private readonly Dictionary<string, double> recommendedDifficultyMapping = new Dictionary<string, double>();
|
||||||
|
|
||||||
|
/// <returns>
|
||||||
|
/// Rulesets ordered descending by their respective recommended difficulties.
|
||||||
|
/// The currently selected ruleset will always be first.
|
||||||
|
/// </returns>
|
||||||
|
private IEnumerable<string> orderedRulesets
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (LoadState < LoadState.Ready || gameRuleset.Value == null)
|
||||||
|
return Enumerable.Empty<string>();
|
||||||
|
|
||||||
|
return recommendedDifficultyMapping
|
||||||
|
.OrderByDescending(pair => pair.Value)
|
||||||
|
.Select(pair => pair.Key)
|
||||||
|
.Where(r => !r.Equals(gameRuleset.Value.ShortName, StringComparison.Ordinal))
|
||||||
|
.Prepend(gameRuleset.Value.ShortName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DifficultyRecommender(LocalUserStatisticsProvider statisticsProvider)
|
||||||
|
{
|
||||||
|
this.statisticsProvider = statisticsProvider;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
api.LocalUser.BindValueChanged(_ => populateValues(), true);
|
foreach (var ruleset in rulesets.AvailableRulesets)
|
||||||
|
{
|
||||||
|
if (statisticsProvider.GetStatisticsFor(ruleset) is UserStatistics statistics)
|
||||||
|
updateMapping(ruleset, statistics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
statisticsProvider.StatisticsUpdated += onStatisticsUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onStatisticsUpdated(UserStatisticsUpdate update) => updateMapping(update.Ruleset, update.NewStatistics);
|
||||||
|
|
||||||
|
private void updateMapping(RulesetInfo ruleset, UserStatistics statistics)
|
||||||
|
{
|
||||||
|
// algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505
|
||||||
|
recommendedDifficultyMapping[ruleset.ShortName] = Math.Pow((double)(statistics.PP ?? 0), 0.4) * 0.195;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -64,35 +111,12 @@ namespace osu.Game.Beatmaps
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateValues()
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
if (api.LocalUser.Value.RulesetsStatistics == null)
|
if (statisticsProvider.IsNotNull())
|
||||||
return;
|
statisticsProvider.StatisticsUpdated -= onStatisticsUpdated;
|
||||||
|
|
||||||
foreach (var kvp in api.LocalUser.Value.RulesetsStatistics)
|
base.Dispose(isDisposing);
|
||||||
{
|
|
||||||
// algorithm taken from https://github.com/ppy/osu-web/blob/e6e2825516449e3d0f3f5e1852c6bdd3428c3437/app/Models/User.php#L1505
|
|
||||||
recommendedDifficultyMapping[kvp.Key] = Math.Pow((double)(kvp.Value.PP ?? 0), 0.4) * 0.195;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <returns>
|
|
||||||
/// Rulesets ordered descending by their respective recommended difficulties.
|
|
||||||
/// The currently selected ruleset will always be first.
|
|
||||||
/// </returns>
|
|
||||||
private IEnumerable<string> orderedRulesets
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (LoadState < LoadState.Ready || ruleset.Value == null)
|
|
||||||
return Enumerable.Empty<string>();
|
|
||||||
|
|
||||||
return recommendedDifficultyMapping
|
|
||||||
.OrderByDescending(pair => pair.Value)
|
|
||||||
.Select(pair => pair.Key)
|
|
||||||
.Where(r => !r.Equals(ruleset.Value.ShortName, StringComparison.Ordinal))
|
|
||||||
.Prepend(ruleset.Value.ShortName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
};
|
};
|
||||||
|
|
||||||
Status = BeatmapOnlineStatus.None;
|
Status = BeatmapOnlineStatus.None;
|
||||||
TextPadding = new MarginPadding { Horizontal = 5, Bottom = 1 };
|
TextPadding = new MarginPadding { Horizontal = 4, Bottom = 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -20,9 +20,9 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
public abstract partial class BeatmapCard : OsuClickableContainer, IHasContextMenu
|
public abstract partial class BeatmapCard : OsuClickableContainer, IHasContextMenu
|
||||||
{
|
{
|
||||||
public const float TRANSITION_DURATION = 340;
|
public const float TRANSITION_DURATION = 340;
|
||||||
public const float CORNER_RADIUS = 10;
|
public const float CORNER_RADIUS = 8;
|
||||||
|
|
||||||
protected const float WIDTH = 430;
|
protected const float WIDTH = 345;
|
||||||
|
|
||||||
public IBindable<bool> Expanded { get; }
|
public IBindable<bool> Expanded { get; }
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
protected override Drawable IdleContent => idleBottomContent;
|
protected override Drawable IdleContent => idleBottomContent;
|
||||||
protected override Drawable DownloadInProgressContent => downloadProgressBar;
|
protected override Drawable DownloadInProgressContent => downloadProgressBar;
|
||||||
|
|
||||||
private const float height = 140;
|
private const float height = 112;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly BeatmapCardContent content;
|
private readonly BeatmapCardContent content;
|
||||||
@ -68,7 +68,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
Padding = new MarginPadding { Right = CORNER_RADIUS },
|
Padding = new MarginPadding { Right = CORNER_RADIUS },
|
||||||
Child = leftIconArea = new FillFlowContainer
|
Child = leftIconArea = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding(5),
|
Margin = new MarginPadding(4),
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(1)
|
Spacing = new Vector2(1)
|
||||||
@ -80,7 +80,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
Width = WIDTH - height + CORNER_RADIUS,
|
Width = WIDTH - height + CORNER_RADIUS,
|
||||||
FavouriteState = { BindTarget = FavouriteState },
|
FavouriteState = { BindTarget = FavouriteState },
|
||||||
ButtonsCollapsedWidth = CORNER_RADIUS,
|
ButtonsCollapsedWidth = CORNER_RADIUS,
|
||||||
ButtonsExpandedWidth = 30,
|
ButtonsExpandedWidth = 24,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
@ -109,7 +109,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
new TruncatingSpriteText
|
new TruncatingSpriteText
|
||||||
{
|
{
|
||||||
Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title),
|
Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title),
|
||||||
Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold),
|
Font = OsuFont.Default.With(size: 18f, weight: FontWeight.SemiBold),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
},
|
},
|
||||||
titleBadgeArea = new FillFlowContainer
|
titleBadgeArea = new FillFlowContainer
|
||||||
@ -142,7 +142,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
new TruncatingSpriteText
|
new TruncatingSpriteText
|
||||||
{
|
{
|
||||||
Text = createArtistText(),
|
Text = createArtistText(),
|
||||||
Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold),
|
Font = OsuFont.Default.With(size: 14f, weight: FontWeight.SemiBold),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
},
|
},
|
||||||
Empty()
|
Empty()
|
||||||
@ -154,7 +154,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Text = BeatmapSet.Source,
|
Text = BeatmapSet.Source,
|
||||||
Shadow = false,
|
Shadow = false,
|
||||||
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold),
|
Font = OsuFont.GetFont(size: 11f, weight: FontWeight.SemiBold),
|
||||||
Colour = colourProvider.Content2
|
Colour = colourProvider.Content2
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -173,18 +173,18 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(0, 3),
|
Spacing = new Vector2(0, 2),
|
||||||
AlwaysPresent = true,
|
AlwaysPresent = true,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new LinkFlowContainer(s =>
|
new LinkFlowContainer(s =>
|
||||||
{
|
{
|
||||||
s.Shadow = false;
|
s.Shadow = false;
|
||||||
s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold);
|
s.Font = OsuFont.GetFont(size: 11f, weight: FontWeight.SemiBold);
|
||||||
}).With(d =>
|
}).With(d =>
|
||||||
{
|
{
|
||||||
d.AutoSizeAxes = Axes.Both;
|
d.AutoSizeAxes = Axes.Both;
|
||||||
d.Margin = new MarginPadding { Top = 2 };
|
d.Margin = new MarginPadding { Top = 1 };
|
||||||
d.AddText("mapped by ", t => t.Colour = colourProvider.Content2);
|
d.AddText("mapped by ", t => t.Colour = colourProvider.Content2);
|
||||||
d.AddUserLink(BeatmapSet.Author);
|
d.AddUserLink(BeatmapSet.Author);
|
||||||
}),
|
}),
|
||||||
@ -215,7 +215,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
downloadProgressBar = new BeatmapCardDownloadProgressBar
|
downloadProgressBar = new BeatmapCardDownloadProgressBar
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = 6,
|
Height = 5,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
State = { BindTarget = DownloadTracker.State },
|
State = { BindTarget = DownloadTracker.State },
|
||||||
@ -231,17 +231,17 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Horizontal = 10, Vertical = 13 },
|
Padding = new MarginPadding { Horizontal = 8, Vertical = 10 },
|
||||||
Child = new BeatmapCardDifficultyList(BeatmapSet)
|
Child = new BeatmapCardDifficultyList(BeatmapSet)
|
||||||
};
|
};
|
||||||
c.Expanded.BindTarget = Expanded;
|
c.Expanded.BindTarget = Expanded;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (BeatmapSet.HasVideo)
|
if (BeatmapSet.HasVideo)
|
||||||
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(20) });
|
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(16) });
|
||||||
|
|
||||||
if (BeatmapSet.HasStoryboard)
|
if (BeatmapSet.HasStoryboard)
|
||||||
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(20) });
|
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(16) });
|
||||||
|
|
||||||
if (BeatmapSet.FeaturedInSpotlight)
|
if (BeatmapSet.FeaturedInSpotlight)
|
||||||
{
|
{
|
||||||
@ -249,7 +249,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
Margin = new MarginPadding { Left = 5 }
|
Margin = new MarginPadding { Left = 4 }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +259,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
Margin = new MarginPadding { Left = 5 }
|
Margin = new MarginPadding { Left = 4 }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +269,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
Margin = new MarginPadding { Left = 5 }
|
Margin = new MarginPadding { Left = 4 }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +288,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
BeatmapCardStatistic withMargin(BeatmapCardStatistic original)
|
BeatmapCardStatistic withMargin(BeatmapCardStatistic original)
|
||||||
{
|
{
|
||||||
original.Margin = new MarginPadding { Right = 10 };
|
original.Margin = new MarginPadding { Right = 8 };
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(4, 0),
|
Spacing = new Vector2(3, 0),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new BeatmapSetOnlineStatusPill
|
new BeatmapSetOnlineStatusPill
|
||||||
@ -33,13 +33,14 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Status = beatmapSet.Status,
|
Status = beatmapSet.Status,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft
|
Origin = Anchor.CentreLeft,
|
||||||
|
TextSize = 13f
|
||||||
},
|
},
|
||||||
new DifficultySpectrumDisplay(beatmapSet)
|
new DifficultySpectrumDisplay(beatmapSet)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
DotSize = new Vector2(6, 12)
|
DotSize = new Vector2(5, 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
protected override Drawable IdleContent => idleBottomContent;
|
protected override Drawable IdleContent => idleBottomContent;
|
||||||
protected override Drawable DownloadInProgressContent => downloadProgressBar;
|
protected override Drawable DownloadInProgressContent => downloadProgressBar;
|
||||||
|
|
||||||
public const float HEIGHT = 100;
|
public const float HEIGHT = 80;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly BeatmapCardContent content;
|
private readonly BeatmapCardContent content;
|
||||||
@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
Padding = new MarginPadding { Right = CORNER_RADIUS },
|
Padding = new MarginPadding { Right = CORNER_RADIUS },
|
||||||
Child = leftIconArea = new FillFlowContainer
|
Child = leftIconArea = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding(5),
|
Margin = new MarginPadding(4),
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(1)
|
Spacing = new Vector2(1)
|
||||||
@ -81,7 +81,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
Width = WIDTH - HEIGHT + CORNER_RADIUS,
|
Width = WIDTH - HEIGHT + CORNER_RADIUS,
|
||||||
FavouriteState = { BindTarget = FavouriteState },
|
FavouriteState = { BindTarget = FavouriteState },
|
||||||
ButtonsCollapsedWidth = CORNER_RADIUS,
|
ButtonsCollapsedWidth = CORNER_RADIUS,
|
||||||
ButtonsExpandedWidth = 30,
|
ButtonsExpandedWidth = 24,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
@ -110,7 +110,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
new TruncatingSpriteText
|
new TruncatingSpriteText
|
||||||
{
|
{
|
||||||
Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title),
|
Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title),
|
||||||
Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold),
|
Font = OsuFont.Default.With(size: 18f, weight: FontWeight.SemiBold),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
},
|
},
|
||||||
titleBadgeArea = new FillFlowContainer
|
titleBadgeArea = new FillFlowContainer
|
||||||
@ -143,7 +143,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
new TruncatingSpriteText
|
new TruncatingSpriteText
|
||||||
{
|
{
|
||||||
Text = createArtistText(),
|
Text = createArtistText(),
|
||||||
Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold),
|
Font = OsuFont.Default.With(size: 14f, weight: FontWeight.SemiBold),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
},
|
},
|
||||||
Empty()
|
Empty()
|
||||||
@ -153,11 +153,11 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
new LinkFlowContainer(s =>
|
new LinkFlowContainer(s =>
|
||||||
{
|
{
|
||||||
s.Shadow = false;
|
s.Shadow = false;
|
||||||
s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold);
|
s.Font = OsuFont.GetFont(size: 11f, weight: FontWeight.SemiBold);
|
||||||
}).With(d =>
|
}).With(d =>
|
||||||
{
|
{
|
||||||
d.AutoSizeAxes = Axes.Both;
|
d.AutoSizeAxes = Axes.Both;
|
||||||
d.Margin = new MarginPadding { Top = 2 };
|
d.Margin = new MarginPadding { Top = 1 };
|
||||||
d.AddText("mapped by ", t => t.Colour = colourProvider.Content2);
|
d.AddText("mapped by ", t => t.Colour = colourProvider.Content2);
|
||||||
d.AddUserLink(BeatmapSet.Author);
|
d.AddUserLink(BeatmapSet.Author);
|
||||||
}),
|
}),
|
||||||
@ -177,7 +177,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(0, 3),
|
Spacing = new Vector2(0, 2),
|
||||||
AlwaysPresent = true,
|
AlwaysPresent = true,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -186,7 +186,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(10, 0),
|
Spacing = new Vector2(8, 0),
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
AlwaysPresent = true,
|
AlwaysPresent = true,
|
||||||
ChildrenEnumerable = createStatistics()
|
ChildrenEnumerable = createStatistics()
|
||||||
@ -197,7 +197,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
downloadProgressBar = new BeatmapCardDownloadProgressBar
|
downloadProgressBar = new BeatmapCardDownloadProgressBar
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = 6,
|
Height = 5,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
State = { BindTarget = DownloadTracker.State },
|
State = { BindTarget = DownloadTracker.State },
|
||||||
@ -213,17 +213,17 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Horizontal = 10, Vertical = 13 },
|
Padding = new MarginPadding { Horizontal = 8, Vertical = 10 },
|
||||||
Child = new BeatmapCardDifficultyList(BeatmapSet)
|
Child = new BeatmapCardDifficultyList(BeatmapSet)
|
||||||
};
|
};
|
||||||
c.Expanded.BindTarget = Expanded;
|
c.Expanded.BindTarget = Expanded;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (BeatmapSet.HasVideo)
|
if (BeatmapSet.HasVideo)
|
||||||
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(20) });
|
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(16) });
|
||||||
|
|
||||||
if (BeatmapSet.HasStoryboard)
|
if (BeatmapSet.HasStoryboard)
|
||||||
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(20) });
|
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(16) });
|
||||||
|
|
||||||
if (BeatmapSet.FeaturedInSpotlight)
|
if (BeatmapSet.FeaturedInSpotlight)
|
||||||
{
|
{
|
||||||
@ -231,7 +231,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
Margin = new MarginPadding { Left = 5 }
|
Margin = new MarginPadding { Left = 4 }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +241,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
Margin = new MarginPadding { Left = 5 }
|
Margin = new MarginPadding { Left = 4 }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
Margin = new MarginPadding { Left = 5 }
|
Margin = new MarginPadding { Left = 4 }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,21 +46,21 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Statistics
|
|||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(5, 0),
|
Spacing = new Vector2(4, 0),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
spriteIcon = new SpriteIcon
|
spriteIcon = new SpriteIcon
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Size = new Vector2(10),
|
Size = new Vector2(8),
|
||||||
Margin = new MarginPadding { Top = 1 }
|
Margin = new MarginPadding { Top = 1 }
|
||||||
},
|
},
|
||||||
spriteText = new OsuSpriteText
|
spriteText = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Font = OsuFont.Default.With(size: 14)
|
Font = OsuFont.Default.With(size: 11)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Objects.Legacy;
|
using osu.Game.Rulesets.Objects.Legacy;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
@ -81,7 +82,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
this.beatmap.BeatmapInfo.BeatmapVersion = FormatVersion;
|
this.beatmap.BeatmapInfo.BeatmapVersion = FormatVersion;
|
||||||
parser = new ConvertHitObjectParser(getOffsetTime(), FormatVersion);
|
parser = new ConvertHitObjectParser(getOffsetTime(), FormatVersion);
|
||||||
|
|
||||||
applyLegacyDefaults(this.beatmap.BeatmapInfo);
|
ApplyLegacyDefaults(this.beatmap);
|
||||||
|
|
||||||
base.ParseStreamInto(stream, beatmap);
|
base.ParseStreamInto(stream, beatmap);
|
||||||
|
|
||||||
@ -189,10 +190,9 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
/// This method's intention is to restore those legacy defaults.
|
/// This method's intention is to restore those legacy defaults.
|
||||||
/// See also: https://osu.ppy.sh/wiki/en/Client/File_formats/Osu_%28file_format%29
|
/// See also: https://osu.ppy.sh/wiki/en/Client/File_formats/Osu_%28file_format%29
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void applyLegacyDefaults(BeatmapInfo beatmapInfo)
|
internal static void ApplyLegacyDefaults(Beatmap beatmap)
|
||||||
{
|
{
|
||||||
beatmapInfo.WidescreenStoryboard = false;
|
beatmap.WidescreenStoryboard = false;
|
||||||
beatmapInfo.SamplesMatchPlaybackRate = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ParseLine(Beatmap beatmap, Section section, string line)
|
protected override void ParseLine(Beatmap beatmap, Section section, string line)
|
||||||
@ -244,7 +244,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case @"AudioLeadIn":
|
case @"AudioLeadIn":
|
||||||
beatmap.BeatmapInfo.AudioLeadIn = Parsing.ParseInt(pair.Value);
|
beatmap.AudioLeadIn = Parsing.ParseInt(pair.Value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"PreviewTime":
|
case @"PreviewTime":
|
||||||
@ -261,7 +261,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case @"StackLeniency":
|
case @"StackLeniency":
|
||||||
beatmap.BeatmapInfo.StackLeniency = Parsing.ParseFloat(pair.Value);
|
beatmap.StackLeniency = Parsing.ParseFloat(pair.Value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"Mode":
|
case @"Mode":
|
||||||
@ -269,31 +269,31 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case @"LetterboxInBreaks":
|
case @"LetterboxInBreaks":
|
||||||
beatmap.BeatmapInfo.LetterboxInBreaks = Parsing.ParseInt(pair.Value) == 1;
|
beatmap.LetterboxInBreaks = Parsing.ParseInt(pair.Value) == 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"SpecialStyle":
|
case @"SpecialStyle":
|
||||||
beatmap.BeatmapInfo.SpecialStyle = Parsing.ParseInt(pair.Value) == 1;
|
beatmap.SpecialStyle = Parsing.ParseInt(pair.Value) == 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"WidescreenStoryboard":
|
case @"WidescreenStoryboard":
|
||||||
beatmap.BeatmapInfo.WidescreenStoryboard = Parsing.ParseInt(pair.Value) == 1;
|
beatmap.WidescreenStoryboard = Parsing.ParseInt(pair.Value) == 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"EpilepsyWarning":
|
case @"EpilepsyWarning":
|
||||||
beatmap.BeatmapInfo.EpilepsyWarning = Parsing.ParseInt(pair.Value) == 1;
|
beatmap.EpilepsyWarning = Parsing.ParseInt(pair.Value) == 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"SamplesMatchPlaybackRate":
|
case @"SamplesMatchPlaybackRate":
|
||||||
beatmap.BeatmapInfo.SamplesMatchPlaybackRate = Parsing.ParseInt(pair.Value) == 1;
|
beatmap.SamplesMatchPlaybackRate = Parsing.ParseInt(pair.Value) == 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"Countdown":
|
case @"Countdown":
|
||||||
beatmap.BeatmapInfo.Countdown = Enum.Parse<CountdownType>(pair.Value);
|
beatmap.Countdown = Enum.Parse<CountdownType>(pair.Value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"CountdownOffset":
|
case @"CountdownOffset":
|
||||||
beatmap.BeatmapInfo.CountdownOffset = Parsing.ParseInt(pair.Value);
|
beatmap.CountdownOffset = Parsing.ParseInt(pair.Value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,7 +313,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case @"DistanceSpacing":
|
case @"DistanceSpacing":
|
||||||
beatmap.BeatmapInfo.DistanceSpacing = Math.Max(0, Parsing.ParseDouble(pair.Value));
|
beatmap.DistanceSpacing = Math.Max(0, Parsing.ParseDouble(pair.Value));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"BeatDivisor":
|
case @"BeatDivisor":
|
||||||
@ -321,11 +321,11 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case @"GridSize":
|
case @"GridSize":
|
||||||
beatmap.BeatmapInfo.GridSize = Parsing.ParseInt(pair.Value);
|
beatmap.GridSize = Parsing.ParseInt(pair.Value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"TimelineZoom":
|
case @"TimelineZoom":
|
||||||
beatmap.BeatmapInfo.TimelineZoom = Math.Max(0, Parsing.ParseDouble(pair.Value));
|
beatmap.TimelineZoom = Math.Max(0, Parsing.ParseDouble(pair.Value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -447,7 +447,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
// Some very old beatmaps had incorrect type specifications for their backgrounds (ie. using 1 for VIDEO
|
// Some very old beatmaps had incorrect type specifications for their backgrounds (ie. using 1 for VIDEO
|
||||||
// instead of 0 for BACKGROUND). To handle this gracefully, check the file extension against known supported
|
// instead of 0 for BACKGROUND). To handle this gracefully, check the file extension against known supported
|
||||||
// video extensions and handle similar to a background if it doesn't match.
|
// video extensions and handle similar to a background if it doesn't match.
|
||||||
if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename).ToLowerInvariant()))
|
if (!SupportedExtensions.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename).ToLowerInvariant()))
|
||||||
{
|
{
|
||||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
|
beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
|
||||||
lineSupportedByEncoder = true;
|
lineSupportedByEncoder = true;
|
||||||
|
@ -79,14 +79,14 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
writer.WriteLine("[General]");
|
writer.WriteLine("[General]");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(beatmap.Metadata.AudioFile)) writer.WriteLine(FormattableString.Invariant($"AudioFilename: {Path.GetFileName(beatmap.Metadata.AudioFile)}"));
|
if (!string.IsNullOrEmpty(beatmap.Metadata.AudioFile)) writer.WriteLine(FormattableString.Invariant($"AudioFilename: {Path.GetFileName(beatmap.Metadata.AudioFile)}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.BeatmapInfo.AudioLeadIn}"));
|
writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.AudioLeadIn}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}"));
|
writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"Countdown: {(int)beatmap.BeatmapInfo.Countdown}"));
|
writer.WriteLine(FormattableString.Invariant($"Countdown: {(int)beatmap.Countdown}"));
|
||||||
writer.WriteLine(FormattableString.Invariant(
|
writer.WriteLine(FormattableString.Invariant(
|
||||||
$"SampleSet: {toLegacySampleBank(((beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePoints.FirstOrDefault() ?? SampleControlPoint.DEFAULT).SampleBank)}"));
|
$"SampleSet: {toLegacySampleBank(((beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePoints.FirstOrDefault() ?? SampleControlPoint.DEFAULT).SampleBank)}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}"));
|
writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.StackLeniency}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"Mode: {onlineRulesetID}"));
|
writer.WriteLine(FormattableString.Invariant($"Mode: {onlineRulesetID}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}"));
|
writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.LetterboxInBreaks ? '1' : '0')}"));
|
||||||
// if (beatmap.BeatmapInfo.UseSkinSprites)
|
// if (beatmap.BeatmapInfo.UseSkinSprites)
|
||||||
// writer.WriteLine(@"UseSkinSprites: 1");
|
// writer.WriteLine(@"UseSkinSprites: 1");
|
||||||
// if (b.AlwaysShowPlayfield)
|
// if (b.AlwaysShowPlayfield)
|
||||||
@ -95,14 +95,14 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
// writer.WriteLine(@"OverlayPosition: " + b.OverlayPosition);
|
// writer.WriteLine(@"OverlayPosition: " + b.OverlayPosition);
|
||||||
// if (!string.IsNullOrEmpty(b.SkinPreference))
|
// if (!string.IsNullOrEmpty(b.SkinPreference))
|
||||||
// writer.WriteLine(@"SkinPreference:" + b.SkinPreference);
|
// writer.WriteLine(@"SkinPreference:" + b.SkinPreference);
|
||||||
if (beatmap.BeatmapInfo.EpilepsyWarning)
|
if (beatmap.EpilepsyWarning)
|
||||||
writer.WriteLine(@"EpilepsyWarning: 1");
|
writer.WriteLine(@"EpilepsyWarning: 1");
|
||||||
if (beatmap.BeatmapInfo.CountdownOffset > 0)
|
if (beatmap.CountdownOffset > 0)
|
||||||
writer.WriteLine(FormattableString.Invariant($@"CountdownOffset: {beatmap.BeatmapInfo.CountdownOffset}"));
|
writer.WriteLine(FormattableString.Invariant($@"CountdownOffset: {beatmap.CountdownOffset}"));
|
||||||
if (onlineRulesetID == 3)
|
if (onlineRulesetID == 3)
|
||||||
writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.BeatmapInfo.SpecialStyle ? '1' : '0')}"));
|
writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.SpecialStyle ? '1' : '0')}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? '1' : '0')}"));
|
writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.WidescreenStoryboard ? '1' : '0')}"));
|
||||||
if (beatmap.BeatmapInfo.SamplesMatchPlaybackRate)
|
if (beatmap.SamplesMatchPlaybackRate)
|
||||||
writer.WriteLine(@"SamplesMatchPlaybackRate: 1");
|
writer.WriteLine(@"SamplesMatchPlaybackRate: 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,10 +112,10 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
if (beatmap.BeatmapInfo.Bookmarks.Length > 0)
|
if (beatmap.BeatmapInfo.Bookmarks.Length > 0)
|
||||||
writer.WriteLine(FormattableString.Invariant($"Bookmarks: {string.Join(',', beatmap.BeatmapInfo.Bookmarks)}"));
|
writer.WriteLine(FormattableString.Invariant($"Bookmarks: {string.Join(',', beatmap.BeatmapInfo.Bookmarks)}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"DistanceSpacing: {beatmap.BeatmapInfo.DistanceSpacing}"));
|
writer.WriteLine(FormattableString.Invariant($"DistanceSpacing: {beatmap.DistanceSpacing}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"BeatDivisor: {beatmap.BeatmapInfo.BeatDivisor}"));
|
writer.WriteLine(FormattableString.Invariant($"BeatDivisor: {beatmap.BeatmapInfo.BeatDivisor}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"GridSize: {beatmap.BeatmapInfo.GridSize}"));
|
writer.WriteLine(FormattableString.Invariant($"GridSize: {beatmap.GridSize}"));
|
||||||
writer.WriteLine(FormattableString.Invariant($"TimelineZoom: {beatmap.BeatmapInfo.TimelineZoom}"));
|
writer.WriteLine(FormattableString.Invariant($"TimelineZoom: {beatmap.TimelineZoom}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMetadata(TextWriter writer)
|
private void handleMetadata(TextWriter writer)
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Beatmaps.Legacy;
|
|||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Storyboards;
|
using osu.Game.Storyboards;
|
||||||
using osu.Game.Storyboards.Commands;
|
using osu.Game.Storyboards.Commands;
|
||||||
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -37,6 +38,17 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
SetFallbackDecoder<Storyboard>(() => new LegacyStoryboardDecoder());
|
SetFallbackDecoder<Storyboard>(() => new LegacyStoryboardDecoder());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Storyboard CreateTemplateObject()
|
||||||
|
{
|
||||||
|
var sb = base.CreateTemplateObject();
|
||||||
|
|
||||||
|
var beatmap = new Beatmap();
|
||||||
|
LegacyBeatmapDecoder.ApplyLegacyDefaults(beatmap);
|
||||||
|
sb.Beatmap = beatmap;
|
||||||
|
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void ParseStreamInto(LineBufferedReader stream, Storyboard storyboard)
|
protected override void ParseStreamInto(LineBufferedReader stream, Storyboard storyboard)
|
||||||
{
|
{
|
||||||
this.storyboard = storyboard;
|
this.storyboard = storyboard;
|
||||||
@ -72,6 +84,10 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
case "UseSkinSprites":
|
case "UseSkinSprites":
|
||||||
storyboard.UseSkinSprites = pair.Value == "1";
|
storyboard.UseSkinSprites = pair.Value == "1";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case @"WidescreenStoryboard":
|
||||||
|
storyboard.Beatmap.WidescreenStoryboard = Parsing.ParseInt(pair.Value) == 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +128,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
//
|
//
|
||||||
// This avoids potential weird crashes when ffmpeg attempts to parse an image file as a video
|
// This avoids potential weird crashes when ffmpeg attempts to parse an image file as a video
|
||||||
// (see https://github.com/ppy/osu/issues/22829#issuecomment-1465552451).
|
// (see https://github.com/ppy/osu/issues/22829#issuecomment-1465552451).
|
||||||
if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path).ToLowerInvariant()))
|
if (!SupportedExtensions.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path).ToLowerInvariant()))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
storyboard.GetLayer("Video").Add(storyboardSprite = new StoryboardVideo(path, offset));
|
storyboard.GetLayer("Video").Add(storyboardSprite = new StoryboardVideo(path, offset));
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using osu.Framework.Lists;
|
using osu.Framework.Lists;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
@ -69,6 +70,43 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
double GetMostCommonBeatLength();
|
double GetMostCommonBeatLength();
|
||||||
|
|
||||||
|
double AudioLeadIn { get; internal set; }
|
||||||
|
|
||||||
|
float StackLeniency { get; internal set; }
|
||||||
|
|
||||||
|
bool SpecialStyle { get; internal set; }
|
||||||
|
|
||||||
|
bool LetterboxInBreaks { get; internal set; }
|
||||||
|
|
||||||
|
bool WidescreenStoryboard { get; internal set; }
|
||||||
|
|
||||||
|
bool EpilepsyWarning { get; internal set; }
|
||||||
|
|
||||||
|
bool SamplesMatchPlaybackRate { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ratio of distance travelled per time unit.
|
||||||
|
/// Generally used to decouple the spacing between hit objects from the enforced "velocity" of the beatmap (see <see cref="DifficultyControlPoint.SliderVelocity"/>).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The most common method of understanding is that at a default value of 1.0, the time-to-distance ratio will match the slider velocity of the beatmap
|
||||||
|
/// at the current point in time. Increasing this value will make hit objects more spaced apart when compared to the cursor movement required to track a slider.
|
||||||
|
///
|
||||||
|
/// This is only a hint property, used by the editor in <see cref="IDistanceSnapProvider"/> implementations. It does not directly affect the beatmap or gameplay.
|
||||||
|
/// </remarks>
|
||||||
|
double DistanceSpacing { get; internal set; }
|
||||||
|
|
||||||
|
int GridSize { get; internal set; }
|
||||||
|
|
||||||
|
double TimelineZoom { get; internal set; }
|
||||||
|
|
||||||
|
CountdownType Countdown { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of beats to move the countdown backwards (compared to its default location).
|
||||||
|
/// </summary>
|
||||||
|
int CountdownOffset { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a shallow-clone of this beatmap and returns it.
|
/// Creates a shallow-clone of this beatmap and returns it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -62,7 +62,12 @@ namespace osu.Game.Beatmaps
|
|||||||
#region Resource getters
|
#region Resource getters
|
||||||
|
|
||||||
protected virtual Waveform GetWaveform() => new Waveform(null);
|
protected virtual Waveform GetWaveform() => new Waveform(null);
|
||||||
protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo };
|
|
||||||
|
protected virtual Storyboard GetStoryboard() => new Storyboard
|
||||||
|
{
|
||||||
|
BeatmapInfo = BeatmapInfo,
|
||||||
|
Beatmap = Beatmap,
|
||||||
|
};
|
||||||
|
|
||||||
protected abstract IBeatmap GetBeatmap();
|
protected abstract IBeatmap GetBeatmap();
|
||||||
public abstract Texture GetBackground();
|
public abstract Texture GetBackground();
|
||||||
|
@ -138,6 +138,7 @@ namespace osu.Game.Configuration
|
|||||||
SetDefault(OsuSetting.LightenDuringBreaks, true);
|
SetDefault(OsuSetting.LightenDuringBreaks, true);
|
||||||
|
|
||||||
SetDefault(OsuSetting.HitLighting, true);
|
SetDefault(OsuSetting.HitLighting, true);
|
||||||
|
SetDefault(OsuSetting.StarFountains, true);
|
||||||
|
|
||||||
SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always);
|
SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always);
|
||||||
SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true);
|
SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true);
|
||||||
@ -214,6 +215,7 @@ namespace osu.Game.Configuration
|
|||||||
SetDefault(OsuSetting.EditorContractSidebars, false);
|
SetDefault(OsuSetting.EditorContractSidebars, false);
|
||||||
|
|
||||||
SetDefault(OsuSetting.AlwaysShowHoldForMenuButton, false);
|
SetDefault(OsuSetting.AlwaysShowHoldForMenuButton, false);
|
||||||
|
SetDefault(OsuSetting.AlwaysRequireHoldingForPause, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CheckLookupContainsPrivateInformation(OsuSetting lookup)
|
protected override bool CheckLookupContainsPrivateInformation(OsuSetting lookup)
|
||||||
@ -413,6 +415,7 @@ namespace osu.Game.Configuration
|
|||||||
NotifyOnPrivateMessage,
|
NotifyOnPrivateMessage,
|
||||||
UIHoldActivationDelay,
|
UIHoldActivationDelay,
|
||||||
HitLighting,
|
HitLighting,
|
||||||
|
StarFountains,
|
||||||
MenuBackgroundSource,
|
MenuBackgroundSource,
|
||||||
GameplayDisableWinKey,
|
GameplayDisableWinKey,
|
||||||
SeasonalBackgroundMode,
|
SeasonalBackgroundMode,
|
||||||
@ -444,5 +447,6 @@ namespace osu.Game.Configuration
|
|||||||
EditorRotationOrigin,
|
EditorRotationOrigin,
|
||||||
EditorTimelineShowBreaks,
|
EditorTimelineShowBreaks,
|
||||||
EditorAdjustExistingObjectsOnTimingChanges,
|
EditorAdjustExistingObjectsOnTimingChanges,
|
||||||
|
AlwaysRequireHoldingForPause
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Online.API.Requests.Responses;
|
|||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
namespace osu.Game.Configuration
|
namespace osu.Game.Configuration
|
||||||
{
|
{
|
||||||
@ -77,7 +78,8 @@ namespace osu.Game.Configuration
|
|||||||
TouchInputActive,
|
TouchInputActive,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stores the local user's last score (can be completed or aborted).
|
/// Contains the local user's last score (can be completed or aborted) after exiting <see cref="Player"/>.
|
||||||
|
/// Will be cleared to <c>null</c> when leaving <see cref="PlayerLoader"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
LastLocalUserScore,
|
LastLocalUserScore,
|
||||||
|
|
||||||
|
@ -94,8 +94,9 @@ namespace osu.Game.Database
|
|||||||
/// 41 2024-04-17 Add ScoreInfo.TotalScoreWithoutMods for future mod multiplier rebalances.
|
/// 41 2024-04-17 Add ScoreInfo.TotalScoreWithoutMods for future mod multiplier rebalances.
|
||||||
/// 42 2024-08-07 Update mania key bindings to reflect changes to ManiaAction
|
/// 42 2024-08-07 Update mania key bindings to reflect changes to ManiaAction
|
||||||
/// 43 2024-10-14 Reset keybind for toggling FPS display to avoid conflict with "convert to stream" in the editor, if not already changed by user.
|
/// 43 2024-10-14 Reset keybind for toggling FPS display to avoid conflict with "convert to stream" in the editor, if not already changed by user.
|
||||||
|
/// 44 2024-11-22 Removed several properties from BeatmapInfo which did not need to be persisted to realm.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int schema_version = 43;
|
private const int schema_version = 44;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
|
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
|
||||||
|
@ -245,8 +245,8 @@ namespace osu.Game.Database
|
|||||||
var scoreProcessor = ruleset.CreateScoreProcessor();
|
var scoreProcessor = ruleset.CreateScoreProcessor();
|
||||||
|
|
||||||
// warning: ordering is important here - both total score and ranks are dependent on accuracy!
|
// warning: ordering is important here - both total score and ranks are dependent on accuracy!
|
||||||
score.Accuracy = computeAccuracy(score, scoreProcessor);
|
score.Accuracy = ComputeAccuracy(score, scoreProcessor);
|
||||||
score.Rank = computeRank(score, scoreProcessor);
|
score.Rank = ComputeRank(score, scoreProcessor);
|
||||||
(score.TotalScoreWithoutMods, score.TotalScore) = convertFromLegacyTotalScore(score, ruleset, beatmap);
|
(score.TotalScoreWithoutMods, score.TotalScore) = convertFromLegacyTotalScore(score, ruleset, beatmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,8 +269,8 @@ namespace osu.Game.Database
|
|||||||
var scoreProcessor = ruleset.CreateScoreProcessor();
|
var scoreProcessor = ruleset.CreateScoreProcessor();
|
||||||
|
|
||||||
// warning: ordering is important here - both total score and ranks are dependent on accuracy!
|
// warning: ordering is important here - both total score and ranks are dependent on accuracy!
|
||||||
score.Accuracy = computeAccuracy(score, scoreProcessor);
|
score.Accuracy = ComputeAccuracy(score, scoreProcessor);
|
||||||
score.Rank = computeRank(score, scoreProcessor);
|
score.Rank = ComputeRank(score, scoreProcessor);
|
||||||
(score.TotalScoreWithoutMods, score.TotalScore) = convertFromLegacyTotalScore(score, ruleset, difficulty, attributes);
|
(score.TotalScoreWithoutMods, score.TotalScore) = convertFromLegacyTotalScore(score, ruleset, difficulty, attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +313,8 @@ namespace osu.Game.Database
|
|||||||
/// <param name="difficulty">The beatmap difficulty.</param>
|
/// <param name="difficulty">The beatmap difficulty.</param>
|
||||||
/// <param name="attributes">The legacy scoring attributes for the beatmap which the score was set on.</param>
|
/// <param name="attributes">The legacy scoring attributes for the beatmap which the score was set on.</param>
|
||||||
/// <returns>The standardised total score.</returns>
|
/// <returns>The standardised total score.</returns>
|
||||||
private static (long withoutMods, long withMods) convertFromLegacyTotalScore(ScoreInfo score, Ruleset ruleset, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes)
|
private static (long withoutMods, long withMods) convertFromLegacyTotalScore(ScoreInfo score, Ruleset ruleset, LegacyBeatmapConversionDifficultyInfo difficulty,
|
||||||
|
LegacyScoreAttributes attributes)
|
||||||
{
|
{
|
||||||
if (!score.IsLegacyScore)
|
if (!score.IsLegacyScore)
|
||||||
return (score.TotalScoreWithoutMods, score.TotalScore);
|
return (score.TotalScoreWithoutMods, score.TotalScore);
|
||||||
@ -620,24 +621,31 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double computeAccuracy(ScoreInfo scoreInfo, ScoreProcessor scoreProcessor)
|
public static double ComputeAccuracy(ScoreInfo scoreInfo, ScoreProcessor scoreProcessor)
|
||||||
|
=> ComputeAccuracy(scoreInfo.Statistics, scoreInfo.MaximumStatistics, scoreProcessor);
|
||||||
|
|
||||||
|
public static double ComputeAccuracy(IReadOnlyDictionary<HitResult, int> statistics, IReadOnlyDictionary<HitResult, int> maximumStatistics, ScoreProcessor scoreProcessor)
|
||||||
{
|
{
|
||||||
int baseScore = scoreInfo.Statistics.Where(kvp => kvp.Key.AffectsAccuracy())
|
int baseScore = statistics.Where(kvp => kvp.Key.AffectsAccuracy())
|
||||||
.Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key));
|
.Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key));
|
||||||
int maxBaseScore = scoreInfo.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy())
|
int maxBaseScore = maximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy())
|
||||||
.Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key));
|
.Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key));
|
||||||
|
|
||||||
return maxBaseScore == 0 ? 1 : baseScore / (double)maxBaseScore;
|
return maxBaseScore == 0 ? 1 : baseScore / (double)maxBaseScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ScoreRank ComputeRank(ScoreInfo scoreInfo) => computeRank(scoreInfo, scoreInfo.Ruleset.CreateInstance().CreateScoreProcessor());
|
public static ScoreRank ComputeRank(ScoreInfo scoreInfo) =>
|
||||||
|
ComputeRank(scoreInfo.Accuracy, scoreInfo.Statistics, scoreInfo.Mods, scoreInfo.Ruleset.CreateInstance().CreateScoreProcessor());
|
||||||
|
|
||||||
private static ScoreRank computeRank(ScoreInfo scoreInfo, ScoreProcessor scoreProcessor)
|
public static ScoreRank ComputeRank(ScoreInfo scoreInfo, ScoreProcessor processor) =>
|
||||||
|
ComputeRank(scoreInfo.Accuracy, scoreInfo.Statistics, scoreInfo.Mods, processor);
|
||||||
|
|
||||||
|
public static ScoreRank ComputeRank(double accuracy, IReadOnlyDictionary<HitResult, int> statistics, IList<Mod> mods, ScoreProcessor scoreProcessor)
|
||||||
{
|
{
|
||||||
var rank = scoreProcessor.RankFromScore(scoreInfo.Accuracy, scoreInfo.Statistics);
|
var rank = scoreProcessor.RankFromScore(accuracy, statistics);
|
||||||
|
|
||||||
foreach (var mod in scoreInfo.Mods.OfType<IApplicableToScoreProcessor>())
|
foreach (var mod in mods.OfType<IApplicableToScoreProcessor>())
|
||||||
rank = mod.AdjustRank(rank, scoreInfo.Accuracy);
|
rank = mod.AdjustRank(rank, accuracy);
|
||||||
|
|
||||||
return rank;
|
return rank;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Graphics.Containers;
|
|||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterfaceV2.FileSelection;
|
using osu.Game.Graphics.UserInterfaceV2.FileSelection;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterfaceV2
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
{
|
{
|
||||||
@ -96,24 +97,18 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (OsuGameBase.VIDEO_EXTENSIONS.Contains(File.Extension.ToLowerInvariant()))
|
string extension = File.Extension.ToLowerInvariant();
|
||||||
|
|
||||||
|
if (SupportedExtensions.VIDEO_EXTENSIONS.Contains(extension))
|
||||||
return FontAwesome.Regular.FileVideo;
|
return FontAwesome.Regular.FileVideo;
|
||||||
|
|
||||||
switch (File.Extension)
|
if (SupportedExtensions.AUDIO_EXTENSIONS.Contains(extension))
|
||||||
{
|
return FontAwesome.Regular.FileAudio;
|
||||||
case @".ogg":
|
|
||||||
case @".mp3":
|
|
||||||
case @".wav":
|
|
||||||
return FontAwesome.Regular.FileAudio;
|
|
||||||
|
|
||||||
case @".jpg":
|
if (SupportedExtensions.IMAGE_EXTENSIONS.Contains(extension))
|
||||||
case @".jpeg":
|
return FontAwesome.Regular.FileImage;
|
||||||
case @".png":
|
|
||||||
return FontAwesome.Regular.FileImage;
|
|
||||||
|
|
||||||
default:
|
return FontAwesome.Regular.File;
|
||||||
return FontAwesome.Regular.File;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +74,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString FadePlayfieldWhenHealthLow => new TranslatableString(getKey(@"fade_playfield_when_health_low"), @"Fade playfield to red when health is low");
|
public static LocalisableString FadePlayfieldWhenHealthLow => new TranslatableString(getKey(@"fade_playfield_when_health_low"), @"Fade playfield to red when health is low");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Star fountains"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString StarFountains => new TranslatableString(getKey(@"star_fountains"), @"Star fountains");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Always show key overlay"
|
/// "Always show key overlay"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -89,6 +94,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString AlwaysShowHoldForMenuButton => new TranslatableString(getKey(@"always_show_hold_for_menu_button"), @"Always show hold for menu button");
|
public static LocalisableString AlwaysShowHoldForMenuButton => new TranslatableString(getKey(@"always_show_hold_for_menu_button"), @"Always show hold for menu button");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Require holding key to pause gameplay"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString AlwaysRequireHoldForMenu => new TranslatableString(getKey(@"require_holding_key_to_pause_gameplay"), @"Require holding key to pause gameplay");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Always play first combo break sound"
|
/// "Always play first combo break sound"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -44,6 +44,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString CheckUpdate => new TranslatableString(getKey(@"check_update"), @"Check for updates");
|
public static LocalisableString CheckUpdate => new TranslatableString(getKey(@"check_update"), @"Check for updates");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Checking for updates"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString CheckingForUpdates => new TranslatableString(getKey(@"checking_for_updates"), @"Checking for updates");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Open osu! folder"
|
/// "Open osu! folder"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
154
osu.Game/Localisation/MenuTipStrings.cs
Normal file
154
osu.Game/Localisation/MenuTipStrings.cs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// 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.Localisation;
|
||||||
|
|
||||||
|
namespace osu.Game.Localisation
|
||||||
|
{
|
||||||
|
public static class MenuTipStrings
|
||||||
|
{
|
||||||
|
private const string prefix = @"osu.Game.Resources.Localisation.MenuTip";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Press Ctrl-T anywhere in the game to toggle the toolbar!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ToggleToolbarShortcut => new TranslatableString(getKey(@"toggle_toolbar_shortcut"), @"Press Ctrl-T anywhere in the game to toggle the toolbar!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Press Ctrl-O anywhere in the game to access settings!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString GameSettingsShortcut => new TranslatableString(getKey(@"game_settings_shortcut"), @"Press Ctrl-O anywhere in the game to access settings!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "All settings are dynamic and take effect in real-time. Try changing the skin while watching autoplay!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString DynamicSettings => new TranslatableString(getKey(@"dynamic_settings"), @"All settings are dynamic and take effect in real-time. Try changing the skin while watching autoplay!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "New features are coming online every update. Make sure to stay up-to-date!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString NewFeaturesAreComingOnline => new TranslatableString(getKey(@"new_features_are_coming_online"), @"New features are coming online every update. Make sure to stay up-to-date!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "If you find the UI too large or small, try adjusting UI scale in settings!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString UIScalingSettings => new TranslatableString(getKey(@"ui_scaling_settings"), @"If you find the UI too large or small, try adjusting UI scale in settings!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Try adjusting the "Screen Scaling" mode to change your gameplay or UI area, even in fullscreen!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ScreenScalingSettings => new TranslatableString(getKey(@"screen_scaling_settings"), @"Try adjusting the ""Screen Scaling"" mode to change your gameplay or UI area, even in fullscreen!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "What used to be "osu!direct" is available to all users just like on the website. You can access it anywhere using Ctrl-B!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString FreeOsuDirect => new TranslatableString(getKey(@"free_osu_direct"), @"What used to be ""osu!direct"" is available to all users just like on the website. You can access it anywhere using Ctrl-B!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Seeking in replays is available by dragging on the progress bar at the bottom of the screen or by using the left and right arrow keys!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ReplaySeeking => new TranslatableString(getKey(@"replay_seeking"), @"Seeking in replays is available by dragging on the progress bar at the bottom of the screen or by using the left and right arrow keys!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Try scrolling right in mod select to find a bunch of new fun mods!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString TryNewMods => new TranslatableString(getKey(@"try_new_mods"), @"Try scrolling right in mod select to find a bunch of new fun mods!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Most of the web content (profiles, rankings, etc.) are available natively in-game from the icons on the toolbar!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString EmbeddedWebContent => new TranslatableString(getKey(@"embedded_web_content"), @"Most of the web content (profiles, rankings, etc.) are available natively in-game from the icons on the toolbar!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Get more details, hide or delete a beatmap by right-clicking on its panel at song select!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString BeatmapRightClick => new TranslatableString(getKey(@"beatmap_right_click"), @"Get more details, hide or delete a beatmap by right-clicking on its panel at song select!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Check out the "playlists" system, which lets users create their own custom and permanent leaderboards!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString DiscoverPlaylists => new TranslatableString(getKey(@"discover_playlists"), @"Check out the ""playlists"" system, which lets users create their own custom and permanent leaderboards!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Toggle advanced frame / thread statistics with Ctrl-F11!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ToggleAdvancedFPSCounter => new TranslatableString(getKey(@"toggle_advanced_fps_counter"), @"Toggle advanced frame / thread statistics with Ctrl-F11!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "You can pause during a replay by pressing Space!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ReplayPausing => new TranslatableString(getKey(@"replay_pausing"), @"You can pause during a replay by pressing Space!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Most of the hotkeys in the game are configurable and can be changed to anything you want. Check the bindings panel under input settings!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ConfigurableHotkeys => new TranslatableString(getKey(@"configurable_hotkeys"), @"Most of the hotkeys in the game are configurable and can be changed to anything you want. Check the bindings panel under input settings!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Your gameplay HUD can be customised by using the skin layout editor. Open it at any time via Ctrl-Shift-S!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString SkinEditor => new TranslatableString(getKey(@"skin_editor"), @"Your gameplay HUD can be customised by using the skin layout editor. Open it at any time via Ctrl-Shift-S!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "You can create mod presets to make toggling your favourite mod combinations easier!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"You can create mod presets to make toggling your favourite mod combinations easier!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Many mods have customisation settings that drastically change how they function. Click the Customise button in mod select to view settings!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ModCustomisationSettings => new TranslatableString(getKey(@"mod_customisation_settings"), @"Many mods have customisation settings that drastically change how they function. Click the Customise button in mod select to view settings!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Press Ctrl-Shift-R to switch to a random skin!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString RandomSkinShortcut => new TranslatableString(getKey(@"random_skin_shortcut"), @"Press Ctrl-Shift-R to switch to a random skin!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "While watching a replay, press Ctrl-H to toggle replay settings!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ToggleReplaySettingsShortcut => new TranslatableString(getKey(@"toggle_replay_settings_shortcut"), @"While watching a replay, press Ctrl-H to toggle replay settings!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "You can easily copy the mods from scores on a leaderboard by right-clicking on them!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString CopyModsFromScore => new TranslatableString(getKey(@"copy_mods_from_score"), @"You can easily copy the mods from scores on a leaderboard by right-clicking on them!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Ctrl-Enter at song select will start a beatmap in autoplay mode!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString AutoplayBeatmapShortcut => new TranslatableString(getKey(@"autoplay_beatmap_shortcut"), @"Ctrl-Enter at song select will start a beatmap in autoplay mode!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Multithreading support means that even with low "FPS" your input and judgements will be accurate!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString MultithreadingSupport => new TranslatableString(getKey(@"multithreading_support"), @"Multithreading support means that even with low ""FPS"" your input and judgements will be accurate!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "All delete operations are temporary until exiting. Restore accidentally deleted content from the maintenance settings!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString TemporaryDeleteOperations => new TranslatableString(getKey(@"temporary_delete_operations"), @"All delete operations are temporary until exiting. Restore accidentally deleted content from the maintenance settings!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Take a look under the hood at performance counters and enable verbose performance logging with Ctrl-F2!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString GlobalStatisticsShortcut => new TranslatableString(getKey(@"global_statistics_shortcut"), @"Take a look under the hood at performance counters and enable verbose performance logging with Ctrl-F2!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "When your gameplay HUD is hidden, you can press and hold Ctrl to view it temporarily!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString PeekHUDWhenHidden => new TranslatableString(getKey(@"peek_hud_when_hidden"), @"When your gameplay HUD is hidden, you can press and hold Ctrl to view it temporarily!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Drag and drop any image into the skin editor to load it in quickly!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString DragAndDropImageInSkinEditor => new TranslatableString(getKey(@"drag_and_drop_image_in_skin_editor"), @"Drag and drop any image into the skin editor to load it in quickly!");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "a tip for you:"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString MenuTipTitle => new TranslatableString(getKey(@"menu_tip_title"), @"a tip for you:");
|
||||||
|
|
||||||
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
|
}
|
||||||
|
}
|
@ -80,9 +80,9 @@ namespace osu.Game.Localisation
|
|||||||
public static LocalisableString TimingBasedColouring => new TranslatableString(getKey(@"Timing_based_colouring"), @"Timing-based note colouring");
|
public static LocalisableString TimingBasedColouring => new TranslatableString(getKey(@"Timing_based_colouring"), @"Timing-based note colouring");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "{0}ms (speed {1})"
|
/// "{0}ms (speed {1:N1})"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString ScrollSpeedTooltip(int scrollTime, int scrollSpeed) => new TranslatableString(getKey(@"ruleset"), @"{0}ms (speed {1})", scrollTime, scrollSpeed);
|
public static LocalisableString ScrollSpeedTooltip(int scrollTime, double scrollSpeed) => new TranslatableString(getKey(@"ruleset"), @"{0}ms (speed {1:N1})", scrollTime, scrollSpeed);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Touch control scheme"
|
/// "Touch control scheme"
|
||||||
|
@ -59,7 +59,6 @@ namespace osu.Game.Online.API
|
|||||||
public IBindable<APIUser> LocalUser => localUser;
|
public IBindable<APIUser> LocalUser => localUser;
|
||||||
public IBindableList<APIRelation> Friends => friends;
|
public IBindableList<APIRelation> Friends => friends;
|
||||||
public IBindable<UserActivity> Activity => activity;
|
public IBindable<UserActivity> Activity => activity;
|
||||||
public IBindable<UserStatistics> Statistics => statistics;
|
|
||||||
|
|
||||||
public INotificationsClient NotificationsClient { get; }
|
public INotificationsClient NotificationsClient { get; }
|
||||||
|
|
||||||
@ -74,8 +73,6 @@ namespace osu.Game.Online.API
|
|||||||
private Bindable<UserStatus?> configStatus { get; } = new Bindable<UserStatus?>();
|
private Bindable<UserStatus?> configStatus { get; } = new Bindable<UserStatus?>();
|
||||||
private Bindable<UserStatus?> localUserStatus { get; } = new Bindable<UserStatus?>();
|
private Bindable<UserStatus?> localUserStatus { get; } = new Bindable<UserStatus?>();
|
||||||
|
|
||||||
private Bindable<UserStatistics> statistics { get; } = new Bindable<UserStatistics>();
|
|
||||||
|
|
||||||
protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password));
|
protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password));
|
||||||
|
|
||||||
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();
|
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();
|
||||||
@ -604,14 +601,6 @@ namespace osu.Game.Online.API
|
|||||||
flushQueue();
|
flushQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateStatistics(UserStatistics newStatistics)
|
|
||||||
{
|
|
||||||
statistics.Value = newStatistics;
|
|
||||||
|
|
||||||
if (IsLoggedIn)
|
|
||||||
localUser.Value.Statistics = newStatistics;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateLocalFriends()
|
public void UpdateLocalFriends()
|
||||||
{
|
{
|
||||||
if (!IsLoggedIn)
|
if (!IsLoggedIn)
|
||||||
@ -630,11 +619,7 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
private static APIUser createGuestUser() => new GuestUser();
|
private static APIUser createGuestUser() => new GuestUser();
|
||||||
|
|
||||||
private void setLocalUser(APIUser user) => Scheduler.Add(() =>
|
private void setLocalUser(APIUser user) => Scheduler.Add(() => localUser.Value = user, false);
|
||||||
{
|
|
||||||
localUser.Value = user;
|
|
||||||
statistics.Value = user.Statistics;
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
|
@ -30,8 +30,6 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public Bindable<UserActivity> Activity { get; } = new Bindable<UserActivity>();
|
public Bindable<UserActivity> Activity { get; } = new Bindable<UserActivity>();
|
||||||
|
|
||||||
public Bindable<UserStatistics?> Statistics { get; } = new Bindable<UserStatistics?>();
|
|
||||||
|
|
||||||
public DummyNotificationsClient NotificationsClient { get; } = new DummyNotificationsClient();
|
public DummyNotificationsClient NotificationsClient { get; } = new DummyNotificationsClient();
|
||||||
INotificationsClient IAPIProvider.NotificationsClient => NotificationsClient;
|
INotificationsClient IAPIProvider.NotificationsClient => NotificationsClient;
|
||||||
|
|
||||||
@ -178,11 +176,6 @@ namespace osu.Game.Online.API
|
|||||||
private void onSuccessfulLogin()
|
private void onSuccessfulLogin()
|
||||||
{
|
{
|
||||||
state.Value = APIState.Online;
|
state.Value = APIState.Online;
|
||||||
Statistics.Value = new UserStatistics
|
|
||||||
{
|
|
||||||
GlobalRank = 1,
|
|
||||||
CountryRank = 1
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Logout()
|
public void Logout()
|
||||||
@ -193,14 +186,6 @@ namespace osu.Game.Online.API
|
|||||||
LocalUser.Value = new GuestUser();
|
LocalUser.Value = new GuestUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateStatistics(UserStatistics newStatistics)
|
|
||||||
{
|
|
||||||
Statistics.Value = newStatistics;
|
|
||||||
|
|
||||||
if (IsLoggedIn)
|
|
||||||
LocalUser.Value.Statistics = newStatistics;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateLocalFriends()
|
public void UpdateLocalFriends()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -220,7 +205,6 @@ namespace osu.Game.Online.API
|
|||||||
IBindable<APIUser> IAPIProvider.LocalUser => LocalUser;
|
IBindable<APIUser> IAPIProvider.LocalUser => LocalUser;
|
||||||
IBindableList<APIRelation> IAPIProvider.Friends => Friends;
|
IBindableList<APIRelation> IAPIProvider.Friends => Friends;
|
||||||
IBindable<UserActivity> IAPIProvider.Activity => Activity;
|
IBindable<UserActivity> IAPIProvider.Activity => Activity;
|
||||||
IBindable<UserStatistics?> IAPIProvider.Statistics => Statistics;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Skip 2FA requirement for next login.
|
/// Skip 2FA requirement for next login.
|
||||||
|
@ -29,11 +29,6 @@ namespace osu.Game.Online.API
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
IBindable<UserActivity> Activity { get; }
|
IBindable<UserActivity> Activity { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The current user's online statistics.
|
|
||||||
/// </summary>
|
|
||||||
IBindable<UserStatistics?> Statistics { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The language supplied by this provider to API requests.
|
/// The language supplied by this provider to API requests.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -129,11 +124,6 @@ namespace osu.Game.Online.API
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
void Logout();
|
void Logout();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets Statistics bindable.
|
|
||||||
/// </summary>
|
|
||||||
void UpdateStatistics(UserStatistics newStatistics);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update the friends status of the current user.
|
/// Update the friends status of the current user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
27
osu.Game/Online/API/Requests/ClosePlaylistRequest.cs
Normal file
27
osu.Game/Online/API/Requests/ClosePlaylistRequest.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// 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.Net.Http;
|
||||||
|
using osu.Framework.IO.Network;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests
|
||||||
|
{
|
||||||
|
public class ClosePlaylistRequest : APIRequest
|
||||||
|
{
|
||||||
|
private readonly long roomId;
|
||||||
|
|
||||||
|
public ClosePlaylistRequest(long roomId)
|
||||||
|
{
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override WebRequest CreateWebRequest()
|
||||||
|
{
|
||||||
|
var request = base.CreateWebRequest();
|
||||||
|
request.Method = HttpMethod.Delete;
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Target => $@"rooms/{roomId}";
|
||||||
|
}
|
||||||
|
}
|
@ -223,8 +223,10 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User statistics for the requested ruleset (in the case of a <see cref="GetUserRequest"/> or <see cref="GetFriendsRequest"/> response).
|
/// User statistics for the requested ruleset (in the case of a <see cref="GetUserRequest"/> or <see cref="GetFriendsRequest"/> response).
|
||||||
/// Otherwise empty.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This returns null when accessed from <see cref="IAPIProvider.LocalUser"/>. Use <see cref="LocalUserStatisticsProvider"/> instead.
|
||||||
|
/// </remarks>
|
||||||
[JsonProperty(@"statistics")]
|
[JsonProperty(@"statistics")]
|
||||||
public UserStatistics Statistics
|
public UserStatistics Statistics
|
||||||
{
|
{
|
||||||
|
@ -161,7 +161,7 @@ namespace osu.Game.Online.Chat
|
|||||||
Messages.AddRange(messages);
|
Messages.AddRange(messages);
|
||||||
|
|
||||||
long? maxMessageId = messages.Max(m => m.Id);
|
long? maxMessageId = messages.Max(m => m.Id);
|
||||||
if (maxMessageId > LastMessageId)
|
if (LastMessageId == null || maxMessageId > LastMessageId)
|
||||||
LastMessageId = maxMessageId;
|
LastMessageId = maxMessageId;
|
||||||
|
|
||||||
purgeOldMessages();
|
purgeOldMessages();
|
||||||
|
92
osu.Game/Online/LocalUserStatisticsProvider.cs
Normal file
92
osu.Game/Online/LocalUserStatisticsProvider.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Online
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A component that keeps track of the latest statistics for the local user.
|
||||||
|
/// </summary>
|
||||||
|
public partial class LocalUserStatisticsProvider : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked whenever a change occured to the statistics of any ruleset,
|
||||||
|
/// either due to change in local user (log out and log in) or as a result of score submission.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This does not guarantee the presence of the old statistics,
|
||||||
|
/// specifically in the case of initial population or change in local user.
|
||||||
|
/// </remarks>
|
||||||
|
public event Action<UserStatisticsUpdate>? StatisticsUpdated;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesets { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; } = null!;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, UserStatistics> statisticsCache = new Dictionary<string, UserStatistics>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the <see cref="UserStatistics"/> currently available for the given ruleset.
|
||||||
|
/// This may return null if the requested statistics has not been fetched before yet.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ruleset">The ruleset to return the corresponding <see cref="UserStatistics"/> for.</param>
|
||||||
|
public UserStatistics? GetStatisticsFor(RulesetInfo ruleset) => statisticsCache.GetValueOrDefault(ruleset.ShortName);
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
api.LocalUser.BindValueChanged(_ =>
|
||||||
|
{
|
||||||
|
// queuing up requests directly on user change is unsafe, as the API status may have not been updated yet.
|
||||||
|
// schedule a frame to allow the API to be in its correct state sending requests.
|
||||||
|
Schedule(initialiseStatistics);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialiseStatistics()
|
||||||
|
{
|
||||||
|
statisticsCache.Clear();
|
||||||
|
|
||||||
|
if (api.LocalUser.Value == null || api.LocalUser.Value.Id <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var ruleset in rulesets.AvailableRulesets.Where(r => r.IsLegacyRuleset()))
|
||||||
|
RefetchStatistics(ruleset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefetchStatistics(RulesetInfo ruleset, Action<UserStatisticsUpdate>? callback = null)
|
||||||
|
{
|
||||||
|
if (!ruleset.IsLegacyRuleset())
|
||||||
|
throw new InvalidOperationException($@"Retrieving statistics is not supported for ruleset {ruleset.ShortName}");
|
||||||
|
|
||||||
|
var request = new GetUserRequest(api.LocalUser.Value.Id, ruleset);
|
||||||
|
request.Success += u => UpdateStatistics(u.Statistics, ruleset, callback);
|
||||||
|
api.Queue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void UpdateStatistics(UserStatistics newStatistics, RulesetInfo ruleset, Action<UserStatisticsUpdate>? callback = null)
|
||||||
|
{
|
||||||
|
var oldStatistics = statisticsCache.GetValueOrDefault(ruleset.ShortName);
|
||||||
|
statisticsCache[ruleset.ShortName] = newStatistics;
|
||||||
|
|
||||||
|
var update = new UserStatisticsUpdate(ruleset, oldStatistics, newStatistics);
|
||||||
|
callback?.Invoke(update);
|
||||||
|
StatisticsUpdated?.Invoke(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record UserStatisticsUpdate(RulesetInfo Ruleset, UserStatistics? OldStatistics, UserStatistics NewStatistics);
|
||||||
|
}
|
@ -366,12 +366,8 @@ namespace osu.Game.Online.Rooms
|
|||||||
{
|
{
|
||||||
RoomID = other.RoomID;
|
RoomID = other.RoomID;
|
||||||
Name = other.Name;
|
Name = other.Name;
|
||||||
|
|
||||||
Category = other.Category;
|
Category = other.Category;
|
||||||
|
Host = other.Host;
|
||||||
if (other.Host != null && Host?.Id != other.Host.Id)
|
|
||||||
Host = other.Host;
|
|
||||||
|
|
||||||
ChannelId = other.ChannelId;
|
ChannelId = other.ChannelId;
|
||||||
Status = other.Status;
|
Status = other.Status;
|
||||||
Availability = other.Availability;
|
Availability = other.Availability;
|
||||||
@ -379,6 +375,7 @@ namespace osu.Game.Online.Rooms
|
|||||||
Type = other.Type;
|
Type = other.Type;
|
||||||
MaxParticipants = other.MaxParticipants;
|
MaxParticipants = other.MaxParticipants;
|
||||||
ParticipantCount = other.ParticipantCount;
|
ParticipantCount = other.ParticipantCount;
|
||||||
|
StartDate = other.StartDate;
|
||||||
EndDate = other.EndDate;
|
EndDate = other.EndDate;
|
||||||
UserScore = other.UserScore;
|
UserScore = other.UserScore;
|
||||||
QueueMode = other.QueueMode;
|
QueueMode = other.QueueMode;
|
||||||
@ -387,22 +384,10 @@ namespace osu.Game.Online.Rooms
|
|||||||
PlaylistItemStats = other.PlaylistItemStats;
|
PlaylistItemStats = other.PlaylistItemStats;
|
||||||
CurrentPlaylistItem = other.CurrentPlaylistItem;
|
CurrentPlaylistItem = other.CurrentPlaylistItem;
|
||||||
AutoSkip = other.AutoSkip;
|
AutoSkip = other.AutoSkip;
|
||||||
|
|
||||||
other.RemoveExpiredPlaylistItems();
|
|
||||||
|
|
||||||
Playlist = other.Playlist;
|
Playlist = other.Playlist;
|
||||||
RecentParticipants = other.RecentParticipants;
|
RecentParticipants = other.RecentParticipants;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveExpiredPlaylistItems()
|
|
||||||
{
|
|
||||||
// Todo: This is not the best way/place to do this, but the intention is to display all playlist items when the room has ended,
|
|
||||||
// and display only the non-expired playlist items while the room is still active. In order to achieve this, all expired items are removed from the source Room.
|
|
||||||
// More refactoring is required before this can be done locally instead - DrawableRoomPlaylist is currently directly bound to the playlist to display items in the room.
|
|
||||||
if (Status is not RoomStatusEnded)
|
|
||||||
Playlist = Playlist.Where(i => !i.Expired).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonObject(MemberSerialization.OptIn)]
|
[JsonObject(MemberSerialization.OptIn)]
|
||||||
public class RoomPlaylistItemStats
|
public class RoomPlaylistItemStats
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Online
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains data about the change in a user's profile statistics after completing a score.
|
/// Contains data about the change in a user's profile statistics after completing a score.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UserStatisticsUpdate
|
public class ScoreBasedUserStatisticsUpdate
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The score set by the user that triggered the update.
|
/// The score set by the user that triggered the update.
|
||||||
@ -27,12 +27,12 @@ namespace osu.Game.Online
|
|||||||
public UserStatistics After { get; }
|
public UserStatistics After { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="UserStatisticsUpdate"/>.
|
/// Creates a new <see cref="ScoreBasedUserStatisticsUpdate"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="score">The score set by the user that triggered the update.</param>
|
/// <param name="score">The score set by the user that triggered the update.</param>
|
||||||
/// <param name="before">The user's profile statistics prior to the score being set.</param>
|
/// <param name="before">The user's profile statistics prior to the score being set.</param>
|
||||||
/// <param name="after">The user's profile statistics after the score was set.</param>
|
/// <param name="after">The user's profile statistics after the score was set.</param>
|
||||||
public UserStatisticsUpdate(ScoreInfo score, UserStatistics before, UserStatistics after)
|
public ScoreBasedUserStatisticsUpdate(ScoreInfo score, UserStatistics before, UserStatistics after)
|
||||||
{
|
{
|
||||||
Score = score;
|
Score = score;
|
||||||
Before = before;
|
Before = before;
|
@ -2,18 +2,14 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
|
||||||
using osu.Game.Online.Spectator;
|
using osu.Game.Online.Spectator;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
|
||||||
|
|
||||||
namespace osu.Game.Online
|
namespace osu.Game.Online
|
||||||
{
|
{
|
||||||
@ -22,8 +18,10 @@ namespace osu.Game.Online
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class UserStatisticsWatcher : Component
|
public partial class UserStatisticsWatcher : Component
|
||||||
{
|
{
|
||||||
public IBindable<UserStatisticsUpdate?> LatestUpdate => latestUpdate;
|
private readonly LocalUserStatisticsProvider statisticsProvider;
|
||||||
private readonly Bindable<UserStatisticsUpdate?> latestUpdate = new Bindable<UserStatisticsUpdate?>();
|
|
||||||
|
public IBindable<ScoreBasedUserStatisticsUpdate?> LatestUpdate => latestUpdate;
|
||||||
|
private readonly Bindable<ScoreBasedUserStatisticsUpdate?> latestUpdate = new Bindable<ScoreBasedUserStatisticsUpdate?>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private SpectatorClient spectatorClient { get; set; } = null!;
|
private SpectatorClient spectatorClient { get; set; } = null!;
|
||||||
@ -33,13 +31,15 @@ namespace osu.Game.Online
|
|||||||
|
|
||||||
private readonly Dictionary<long, ScoreInfo> watchedScores = new Dictionary<long, ScoreInfo>();
|
private readonly Dictionary<long, ScoreInfo> watchedScores = new Dictionary<long, ScoreInfo>();
|
||||||
|
|
||||||
private Dictionary<string, UserStatistics>? latestStatistics;
|
public UserStatisticsWatcher(LocalUserStatisticsProvider statisticsProvider)
|
||||||
|
{
|
||||||
|
this.statisticsProvider = statisticsProvider;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
api.LocalUser.BindValueChanged(user => onUserChanged(user.NewValue), true);
|
|
||||||
spectatorClient.OnUserScoreProcessed += userScoreProcessed;
|
spectatorClient.OnUserScoreProcessed += userScoreProcessed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,35 +61,6 @@ namespace osu.Game.Online
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUserChanged(APIUser? localUser) => Schedule(() =>
|
|
||||||
{
|
|
||||||
latestStatistics = null;
|
|
||||||
|
|
||||||
if (localUser == null || localUser.OnlineID <= 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var userRequest = new GetUsersRequest(new[] { localUser.OnlineID });
|
|
||||||
userRequest.Success += initialiseUserStatistics;
|
|
||||||
api.Queue(userRequest);
|
|
||||||
});
|
|
||||||
|
|
||||||
private void initialiseUserStatistics(GetUsersResponse response) => Schedule(() =>
|
|
||||||
{
|
|
||||||
var user = response.Users.SingleOrDefault();
|
|
||||||
|
|
||||||
// possible if the user is restricted or similar.
|
|
||||||
if (user == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
latestStatistics = new Dictionary<string, UserStatistics>();
|
|
||||||
|
|
||||||
if (user.RulesetsStatistics != null)
|
|
||||||
{
|
|
||||||
foreach (var rulesetStats in user.RulesetsStatistics)
|
|
||||||
latestStatistics.Add(rulesetStats.Key, rulesetStats.Value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
private void userScoreProcessed(int userId, long scoreId)
|
private void userScoreProcessed(int userId, long scoreId)
|
||||||
{
|
{
|
||||||
if (userId != api.LocalUser.Value?.OnlineID)
|
if (userId != api.LocalUser.Value?.OnlineID)
|
||||||
@ -98,30 +69,11 @@ namespace osu.Game.Online
|
|||||||
if (!watchedScores.Remove(scoreId, out var scoreInfo))
|
if (!watchedScores.Remove(scoreId, out var scoreInfo))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
requestStatisticsUpdate(userId, scoreInfo);
|
statisticsProvider.RefetchStatistics(scoreInfo.Ruleset, u => Schedule(() =>
|
||||||
}
|
{
|
||||||
|
if (u.OldStatistics != null)
|
||||||
private void requestStatisticsUpdate(int userId, ScoreInfo scoreInfo)
|
latestUpdate.Value = new ScoreBasedUserStatisticsUpdate(scoreInfo, u.OldStatistics, u.NewStatistics);
|
||||||
{
|
}));
|
||||||
var request = new GetUserRequest(userId, scoreInfo.Ruleset);
|
|
||||||
request.Success += user => Schedule(() => dispatchStatisticsUpdate(scoreInfo, user.Statistics));
|
|
||||||
api.Queue(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dispatchStatisticsUpdate(ScoreInfo scoreInfo, UserStatistics updatedStatistics)
|
|
||||||
{
|
|
||||||
string rulesetName = scoreInfo.Ruleset.ShortName;
|
|
||||||
|
|
||||||
api.UpdateStatistics(updatedStatistics);
|
|
||||||
|
|
||||||
if (latestStatistics == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
latestStatistics.TryGetValue(rulesetName, out UserStatistics? latestRulesetStatistics);
|
|
||||||
latestRulesetStatistics ??= new UserStatistics();
|
|
||||||
|
|
||||||
latestUpdate.Value = new UserStatisticsUpdate(scoreInfo, latestRulesetStatistics, updatedStatistics);
|
|
||||||
latestStatistics[rulesetName] = updatedStatistics;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -148,8 +148,7 @@ namespace osu.Game
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private FrameworkConfigManager frameworkConfig { get; set; }
|
private FrameworkConfigManager frameworkConfig { get; set; }
|
||||||
|
|
||||||
[Cached]
|
private DifficultyRecommender difficultyRecommender;
|
||||||
private readonly DifficultyRecommender difficultyRecommender = new DifficultyRecommender();
|
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly LegacyImportManager legacyImportManager = new LegacyImportManager();
|
private readonly LegacyImportManager legacyImportManager = new LegacyImportManager();
|
||||||
@ -175,6 +174,11 @@ namespace osu.Game
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly IBindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>();
|
public readonly IBindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the back button is currently displayed.
|
||||||
|
/// </summary>
|
||||||
|
private readonly IBindable<bool> backButtonVisibility = new Bindable<bool>();
|
||||||
|
|
||||||
IBindable<LocalUserPlayingState> ILocalUserPlayInfo.PlayingState => playingState;
|
IBindable<LocalUserPlayingState> ILocalUserPlayInfo.PlayingState => playingState;
|
||||||
|
|
||||||
private readonly Bindable<LocalUserPlayingState> playingState = new Bindable<LocalUserPlayingState>();
|
private readonly Bindable<LocalUserPlayingState> playingState = new Bindable<LocalUserPlayingState>();
|
||||||
@ -196,7 +200,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
private MainMenu menuScreen;
|
private MainMenu menuScreen;
|
||||||
|
|
||||||
private VersionManager versionManager;
|
[CanBeNull]
|
||||||
|
private DevBuildBanner devBuildBanner;
|
||||||
|
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
private IntroScreen introScreen;
|
private IntroScreen introScreen;
|
||||||
@ -1019,7 +1024,7 @@ namespace osu.Game
|
|||||||
if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen))
|
if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!((Drawable)currentScreen).IsLoaded || (currentScreen.AllowBackButton && !currentScreen.OnBackButton()))
|
if (!((Drawable)currentScreen).IsLoaded || (currentScreen.AllowUserExit && !currentScreen.OnBackButton()))
|
||||||
ScreenStack.Exit();
|
ScreenStack.Exit();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1056,10 +1061,7 @@ namespace osu.Game
|
|||||||
}, topMostOverlayContent.Add);
|
}, topMostOverlayContent.Add);
|
||||||
|
|
||||||
if (!IsDeployedBuild)
|
if (!IsDeployedBuild)
|
||||||
{
|
loadComponentSingleFile(devBuildBanner = new DevBuildBanner(), ScreenContainer.Add);
|
||||||
dependencies.Cache(versionManager = new VersionManager());
|
|
||||||
loadComponentSingleFile(versionManager, ScreenContainer.Add);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadComponentSingleFile(osuLogo, _ =>
|
loadComponentSingleFile(osuLogo, _ =>
|
||||||
{
|
{
|
||||||
@ -1069,7 +1071,11 @@ namespace osu.Game
|
|||||||
ScreenStack.Push(CreateLoader().With(l => l.RelativeSizeAxes = Axes.Both));
|
ScreenStack.Push(CreateLoader().With(l => l.RelativeSizeAxes = Axes.Both));
|
||||||
});
|
});
|
||||||
|
|
||||||
loadComponentSingleFile(new UserStatisticsWatcher(), Add, true);
|
LocalUserStatisticsProvider statisticsProvider;
|
||||||
|
|
||||||
|
loadComponentSingleFile(statisticsProvider = new LocalUserStatisticsProvider(), Add, true);
|
||||||
|
loadComponentSingleFile(difficultyRecommender = new DifficultyRecommender(statisticsProvider), Add, true);
|
||||||
|
loadComponentSingleFile(new UserStatisticsWatcher(statisticsProvider), Add, true);
|
||||||
loadComponentSingleFile(Toolbar = new Toolbar
|
loadComponentSingleFile(Toolbar = new Toolbar
|
||||||
{
|
{
|
||||||
OnHome = delegate
|
OnHome = delegate
|
||||||
@ -1139,7 +1145,6 @@ namespace osu.Game
|
|||||||
loadComponentSingleFile(new BackgroundDataStoreProcessor(), Add);
|
loadComponentSingleFile(new BackgroundDataStoreProcessor(), Add);
|
||||||
loadComponentSingleFile(new DetachedBeatmapStore(), Add, true);
|
loadComponentSingleFile(new DetachedBeatmapStore(), Add, true);
|
||||||
|
|
||||||
Add(difficultyRecommender);
|
|
||||||
Add(externalLinkOpener = new ExternalLinkOpener());
|
Add(externalLinkOpener = new ExternalLinkOpener());
|
||||||
Add(new MusicKeyBindingHandler());
|
Add(new MusicKeyBindingHandler());
|
||||||
Add(new OnlineStatusNotifier(() => ScreenStack.CurrentScreen));
|
Add(new OnlineStatusNotifier(() => ScreenStack.CurrentScreen));
|
||||||
@ -1189,6 +1194,14 @@ namespace osu.Game
|
|||||||
if (mode.NewValue != OverlayActivation.All) CloseAllOverlays();
|
if (mode.NewValue != OverlayActivation.All) CloseAllOverlays();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
backButtonVisibility.ValueChanged += visible =>
|
||||||
|
{
|
||||||
|
if (visible.NewValue)
|
||||||
|
BackButton.Show();
|
||||||
|
else
|
||||||
|
BackButton.Hide();
|
||||||
|
};
|
||||||
|
|
||||||
// Importantly, this should be run after binding PostNotification to the import handlers so they can present the import after game startup.
|
// Importantly, this should be run after binding PostNotification to the import handlers so they can present the import after game startup.
|
||||||
handleStartupImport();
|
handleStartupImport();
|
||||||
}
|
}
|
||||||
@ -1562,12 +1575,12 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
case IntroScreen intro:
|
case IntroScreen intro:
|
||||||
introScreen = intro;
|
introScreen = intro;
|
||||||
versionManager?.Show();
|
devBuildBanner?.Show();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MainMenu menu:
|
case MainMenu menu:
|
||||||
menuScreen = menu;
|
menuScreen = menu;
|
||||||
versionManager?.Show();
|
devBuildBanner?.Show();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Player player:
|
case Player player:
|
||||||
@ -1575,18 +1588,20 @@ namespace osu.Game
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
versionManager?.Hide();
|
devBuildBanner?.Hide();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current is IOsuScreen currentOsuScreen)
|
if (current is IOsuScreen currentOsuScreen)
|
||||||
{
|
{
|
||||||
|
backButtonVisibility.UnbindFrom(currentOsuScreen.BackButtonVisibility);
|
||||||
OverlayActivationMode.UnbindFrom(currentOsuScreen.OverlayActivationMode);
|
OverlayActivationMode.UnbindFrom(currentOsuScreen.OverlayActivationMode);
|
||||||
API.Activity.UnbindFrom(currentOsuScreen.Activity);
|
API.Activity.UnbindFrom(currentOsuScreen.Activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newScreen is IOsuScreen newOsuScreen)
|
if (newScreen is IOsuScreen newOsuScreen)
|
||||||
{
|
{
|
||||||
|
backButtonVisibility.BindTo(newOsuScreen.BackButtonVisibility);
|
||||||
OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode);
|
OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode);
|
||||||
API.Activity.BindTo(newOsuScreen.Activity);
|
API.Activity.BindTo(newOsuScreen.Activity);
|
||||||
|
|
||||||
@ -1597,11 +1612,6 @@ namespace osu.Game
|
|||||||
else
|
else
|
||||||
Toolbar.Show();
|
Toolbar.Show();
|
||||||
|
|
||||||
if (newOsuScreen.AllowBackButton)
|
|
||||||
BackButton.Show();
|
|
||||||
else
|
|
||||||
BackButton.Hide();
|
|
||||||
|
|
||||||
if (newOsuScreen.ShowFooter)
|
if (newOsuScreen.ShowFooter)
|
||||||
{
|
{
|
||||||
BackButton.Hide();
|
BackButton.Hide();
|
||||||
|
@ -73,8 +73,6 @@ namespace osu.Game
|
|||||||
[Cached(typeof(OsuGameBase))]
|
[Cached(typeof(OsuGameBase))]
|
||||||
public partial class OsuGameBase : Framework.Game, ICanAcceptFiles, IBeatSyncProvider
|
public partial class OsuGameBase : Framework.Game, ICanAcceptFiles, IBeatSyncProvider
|
||||||
{
|
{
|
||||||
public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv", ".mpg", ".wmv", ".m4v" };
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
public const string GAME_NAME = "osu! (development)";
|
public const string GAME_NAME = "osu! (development)";
|
||||||
#else
|
#else
|
||||||
|
@ -198,7 +198,6 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
c.Anchor = Anchor.TopCentre;
|
c.Anchor = Anchor.TopCentre;
|
||||||
c.Origin = Anchor.TopCentre;
|
c.Origin = Anchor.TopCentre;
|
||||||
c.Scale = new Vector2(0.8f);
|
|
||||||
})).ToArray();
|
})).ToArray();
|
||||||
|
|
||||||
private static ReverseChildIDFillFlowContainer<BeatmapCard> createCardContainerFor(IEnumerable<BeatmapCard> newCards)
|
private static ReverseChildIDFillFlowContainer<BeatmapCard> createCardContainerFor(IEnumerable<BeatmapCard> newCards)
|
||||||
|
@ -37,11 +37,13 @@ namespace osu.Game.Overlays.Chat.ChannelList
|
|||||||
|
|
||||||
private readonly Dictionary<Channel, ChannelListItem> channelMap = new Dictionary<Channel, ChannelListItem>();
|
private readonly Dictionary<Channel, ChannelListItem> channelMap = new Dictionary<Channel, ChannelListItem>();
|
||||||
|
|
||||||
|
public ChannelGroup AnnounceChannelGroup { get; private set; } = null!;
|
||||||
|
public ChannelGroup PublicChannelGroup { get; private set; } = null!;
|
||||||
|
public ChannelGroup PrivateChannelGroup { get; private set; } = null!;
|
||||||
|
|
||||||
private OsuScrollContainer scroll = null!;
|
private OsuScrollContainer scroll = null!;
|
||||||
private SearchContainer groupFlow = null!;
|
private SearchContainer groupFlow = null!;
|
||||||
private ChannelGroup announceChannelGroup = null!;
|
|
||||||
private ChannelGroup publicChannelGroup = null!;
|
|
||||||
private ChannelGroup privateChannelGroup = null!;
|
|
||||||
private ChannelListItem selector = null!;
|
private ChannelListItem selector = null!;
|
||||||
private TextBox searchTextBox = null!;
|
private TextBox searchTextBox = null!;
|
||||||
|
|
||||||
@ -77,10 +79,10 @@ namespace osu.Game.Overlays.Chat.ChannelList
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
announceChannelGroup = new ChannelGroup(ChatStrings.ChannelsListTitleANNOUNCE.ToUpper()),
|
AnnounceChannelGroup = new ChannelGroup(ChatStrings.ChannelsListTitleANNOUNCE.ToUpper(), false),
|
||||||
publicChannelGroup = new ChannelGroup(ChatStrings.ChannelsListTitlePUBLIC.ToUpper()),
|
PublicChannelGroup = new ChannelGroup(ChatStrings.ChannelsListTitlePUBLIC.ToUpper(), false),
|
||||||
selector = new ChannelListItem(ChannelListingChannel),
|
selector = new ChannelListItem(ChannelListingChannel),
|
||||||
privateChannelGroup = new ChannelGroup(ChatStrings.ChannelsListTitlePM.ToUpper()),
|
PrivateChannelGroup = new ChannelGroup(ChatStrings.ChannelsListTitlePM.ToUpper(), true),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -111,69 +113,70 @@ namespace osu.Game.Overlays.Chat.ChannelList
|
|||||||
item.OnRequestSelect += chan => OnRequestSelect?.Invoke(chan);
|
item.OnRequestSelect += chan => OnRequestSelect?.Invoke(chan);
|
||||||
item.OnRequestLeave += chan => OnRequestLeave?.Invoke(chan);
|
item.OnRequestLeave += chan => OnRequestLeave?.Invoke(chan);
|
||||||
|
|
||||||
FillFlowContainer<ChannelListItem> flow = getFlowForChannel(channel);
|
ChannelGroup group = getGroupFromChannel(channel);
|
||||||
channelMap.Add(channel, item);
|
channelMap.Add(channel, item);
|
||||||
flow.Add(item);
|
group.AddChannel(item);
|
||||||
|
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveChannel(Channel channel)
|
public void RemoveChannel(Channel channel)
|
||||||
{
|
{
|
||||||
if (!channelMap.ContainsKey(channel))
|
if (!channelMap.TryGetValue(channel, out var item))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChannelListItem item = channelMap[channel];
|
ChannelGroup group = getGroupFromChannel(channel);
|
||||||
FillFlowContainer<ChannelListItem> flow = getFlowForChannel(channel);
|
|
||||||
|
|
||||||
channelMap.Remove(channel);
|
channelMap.Remove(channel);
|
||||||
flow.Remove(item, true);
|
group.RemoveChannel(item);
|
||||||
|
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelListItem GetItem(Channel channel)
|
public ChannelListItem GetItem(Channel channel)
|
||||||
{
|
{
|
||||||
if (!channelMap.ContainsKey(channel))
|
if (!channelMap.TryGetValue(channel, out var item))
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
return channelMap[channel];
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ScrollChannelIntoView(Channel channel) => scroll.ScrollIntoView(GetItem(channel));
|
public void ScrollChannelIntoView(Channel channel) => scroll.ScrollIntoView(GetItem(channel));
|
||||||
|
|
||||||
private FillFlowContainer<ChannelListItem> getFlowForChannel(Channel channel)
|
private ChannelGroup getGroupFromChannel(Channel channel)
|
||||||
{
|
{
|
||||||
switch (channel.Type)
|
switch (channel.Type)
|
||||||
{
|
{
|
||||||
case ChannelType.Public:
|
case ChannelType.Public:
|
||||||
return publicChannelGroup.ItemFlow;
|
return PublicChannelGroup;
|
||||||
|
|
||||||
case ChannelType.PM:
|
case ChannelType.PM:
|
||||||
return privateChannelGroup.ItemFlow;
|
return PrivateChannelGroup;
|
||||||
|
|
||||||
case ChannelType.Announce:
|
case ChannelType.Announce:
|
||||||
return announceChannelGroup.ItemFlow;
|
return AnnounceChannelGroup;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return publicChannelGroup.ItemFlow;
|
return PublicChannelGroup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateVisibility()
|
private void updateVisibility()
|
||||||
{
|
{
|
||||||
if (announceChannelGroup.ItemFlow.Children.Count == 0)
|
if (AnnounceChannelGroup.ItemFlow.Children.Count == 0)
|
||||||
announceChannelGroup.Hide();
|
AnnounceChannelGroup.Hide();
|
||||||
else
|
else
|
||||||
announceChannelGroup.Show();
|
AnnounceChannelGroup.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class ChannelGroup : FillFlowContainer
|
public partial class ChannelGroup : FillFlowContainer
|
||||||
{
|
{
|
||||||
public readonly FillFlowContainer<ChannelListItem> ItemFlow;
|
private readonly bool sortByRecent;
|
||||||
|
public readonly ChannelListItemFlow ItemFlow;
|
||||||
|
|
||||||
public ChannelGroup(LocalisableString label)
|
public ChannelGroup(LocalisableString label, bool sortByRecent)
|
||||||
{
|
{
|
||||||
|
this.sortByRecent = sortByRecent;
|
||||||
Direction = FillDirection.Vertical;
|
Direction = FillDirection.Vertical;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
@ -187,7 +190,7 @@ namespace osu.Game.Overlays.Chat.ChannelList
|
|||||||
Margin = new MarginPadding { Left = 18, Bottom = 5 },
|
Margin = new MarginPadding { Left = 18, Bottom = 5 },
|
||||||
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold),
|
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold),
|
||||||
},
|
},
|
||||||
ItemFlow = new FillFlowContainer<ChannelListItem>
|
ItemFlow = new ChannelListItemFlow(sortByRecent)
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
@ -195,6 +198,60 @@ namespace osu.Game.Overlays.Chat.ChannelList
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public partial class ChannelListItemFlow : FillFlowContainer<ChannelListItem>
|
||||||
|
{
|
||||||
|
private readonly bool sortByRecent;
|
||||||
|
|
||||||
|
public ChannelListItemFlow(bool sortByRecent)
|
||||||
|
{
|
||||||
|
this.sortByRecent = sortByRecent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reflow() => InvalidateLayout();
|
||||||
|
|
||||||
|
public override IEnumerable<Drawable> FlowingChildren => sortByRecent
|
||||||
|
? base.FlowingChildren.OfType<ChannelListItem>().OrderByDescending(i => i.Channel.LastMessageId ?? long.MinValue)
|
||||||
|
: base.FlowingChildren.OfType<ChannelListItem>().OrderBy(i => i.Channel.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddChannel(ChannelListItem item)
|
||||||
|
{
|
||||||
|
ItemFlow.Add(item);
|
||||||
|
|
||||||
|
if (sortByRecent)
|
||||||
|
{
|
||||||
|
item.Channel.NewMessagesArrived += newMessagesArrived;
|
||||||
|
item.Channel.PendingMessageResolved += pendingMessageResolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemFlow.Reflow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveChannel(ChannelListItem item)
|
||||||
|
{
|
||||||
|
if (sortByRecent)
|
||||||
|
{
|
||||||
|
item.Channel.NewMessagesArrived -= newMessagesArrived;
|
||||||
|
item.Channel.PendingMessageResolved -= pendingMessageResolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemFlow.Remove(item, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pendingMessageResolved(LocalEchoMessage _, Message __) => ItemFlow.Reflow();
|
||||||
|
private void newMessagesArrived(IEnumerable<Message> _) => ItemFlow.Reflow();
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
foreach (var item in ItemFlow)
|
||||||
|
{
|
||||||
|
item.Channel.NewMessagesArrived -= newMessagesArrived;
|
||||||
|
item.Channel.PendingMessageResolved -= pendingMessageResolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class ChannelSearchTextBox : BasicSearchTextBox
|
private partial class ChannelSearchTextBox : BasicSearchTextBox
|
||||||
|
58
osu.Game/Overlays/DevBuildBanner.cs
Normal file
58
osu.Game/Overlays/DevBuildBanner.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays
|
||||||
|
{
|
||||||
|
public partial class DevBuildBanner : VisibilityContainer
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours, TextureStore textures, OsuGameBase game)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Anchor = Anchor.BottomCentre;
|
||||||
|
Origin = Anchor.BottomCentre;
|
||||||
|
|
||||||
|
Alpha = 0;
|
||||||
|
|
||||||
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Font = OsuFont.Numeric.With(weight: FontWeight.Bold, size: 12),
|
||||||
|
Colour = colours.YellowDark,
|
||||||
|
Text = @"DEVELOPER BUILD",
|
||||||
|
},
|
||||||
|
new Sprite
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Texture = textures.Get(@"Menu/dev-build-footer"),
|
||||||
|
Scale = new Vector2(0.4f, 1),
|
||||||
|
Y = 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
this.FadeIn(1400, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
this.FadeOut(500, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -138,34 +138,31 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
topFifty.ValueColour = colourProvider.Content2;
|
topFifty.ValueColour = colourProvider.Content2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reference: https://github.com/ppy/osu-web/blob/adf1e94754ba9625b85eba795f4a310caf169eec/resources/js/profile-page/daily-challenge.tsx#L13-L47
|
// reference: https://github.com/ppy/osu-web/blob/a97f156014e00ea1aa315140da60542e798a9f06/resources/js/profile-page/daily-challenge.tsx#L13-L47
|
||||||
|
|
||||||
// Rounding up is needed here to ensure the overlay shows the same colour as osu-web for the play count.
|
public static RankingTier TierForPlayCount(int playCount) => TierForDaily((int)Math.Floor(playCount / 3.0d));
|
||||||
// This is because, for example, 31 / 3 > 10 in JavaScript because floats are used, while here it would
|
|
||||||
// get truncated to 10 with an integer division and show a lower tier.
|
|
||||||
public static RankingTier TierForPlayCount(int playCount) => TierForDaily((int)Math.Ceiling(playCount / 3.0d));
|
|
||||||
|
|
||||||
public static RankingTier TierForDaily(int daily)
|
public static RankingTier TierForDaily(int daily)
|
||||||
{
|
{
|
||||||
if (daily > 360)
|
if (daily >= 360)
|
||||||
return RankingTier.Lustrous;
|
return RankingTier.Lustrous;
|
||||||
|
|
||||||
if (daily > 240)
|
if (daily >= 240)
|
||||||
return RankingTier.Radiant;
|
return RankingTier.Radiant;
|
||||||
|
|
||||||
if (daily > 120)
|
if (daily >= 120)
|
||||||
return RankingTier.Rhodium;
|
return RankingTier.Rhodium;
|
||||||
|
|
||||||
if (daily > 60)
|
if (daily >= 60)
|
||||||
return RankingTier.Platinum;
|
return RankingTier.Platinum;
|
||||||
|
|
||||||
if (daily > 30)
|
if (daily >= 30)
|
||||||
return RankingTier.Gold;
|
return RankingTier.Gold;
|
||||||
|
|
||||||
if (daily > 10)
|
if (daily >= 10)
|
||||||
return RankingTier.Silver;
|
return RankingTier.Silver;
|
||||||
|
|
||||||
if (daily > 5)
|
if (daily >= 5)
|
||||||
return RankingTier.Bronze;
|
return RankingTier.Bronze;
|
||||||
|
|
||||||
return RankingTier.Iron;
|
return RankingTier.Iron;
|
||||||
|
@ -71,7 +71,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
|
|||||||
? new BeatmapCardNormal(model)
|
? new BeatmapCardNormal(model)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
LabelText = GraphicsSettingsStrings.HitLighting,
|
LabelText = GraphicsSettingsStrings.HitLighting,
|
||||||
Current = config.GetBindable<bool>(OsuSetting.HitLighting)
|
Current = config.GetBindable<bool>(OsuSetting.HitLighting)
|
||||||
},
|
},
|
||||||
|
new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = GameplaySettingsStrings.StarFountains,
|
||||||
|
Current = config.GetBindable<bool>(OsuSetting.StarFountains)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
Current = config.GetBindable<bool>(OsuSetting.GameplayLeaderboard),
|
Current = config.GetBindable<bool>(OsuSetting.GameplayLeaderboard),
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = GameplaySettingsStrings.AlwaysRequireHoldForMenu,
|
||||||
|
Current = config.GetBindable<bool>(OsuSetting.AlwaysRequireHoldingForPause),
|
||||||
|
},
|
||||||
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = GameplaySettingsStrings.AlwaysShowHoldForMenuButton,
|
LabelText = GameplaySettingsStrings.AlwaysShowHoldForMenuButton,
|
||||||
Current = config.GetBindable<bool>(OsuSetting.AlwaysShowHoldForMenuButton),
|
Current = config.GetBindable<bool>(OsuSetting.AlwaysShowHoldForMenuButton),
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
@ -13,6 +12,7 @@ using osu.Framework.Screens;
|
|||||||
using osu.Framework.Statistics;
|
using osu.Framework.Statistics;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Overlays.Settings.Sections.Maintenance;
|
using osu.Game.Overlays.Settings.Sections.Maintenance;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
@ -36,8 +36,11 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private Storage storage { get; set; } = null!;
|
private Storage storage { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuGame? game { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config, OsuGame? game)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
Add(new SettingsEnumDropdown<ReleaseStream>
|
Add(new SettingsEnumDropdown<ReleaseStream>
|
||||||
{
|
{
|
||||||
@ -50,23 +53,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
Add(checkForUpdatesButton = new SettingsButton
|
Add(checkForUpdatesButton = new SettingsButton
|
||||||
{
|
{
|
||||||
Text = GeneralSettingsStrings.CheckUpdate,
|
Text = GeneralSettingsStrings.CheckUpdate,
|
||||||
Action = () =>
|
Action = () => checkForUpdates().FireAndForget()
|
||||||
{
|
|
||||||
checkForUpdatesButton.Enabled.Value = false;
|
|
||||||
Task.Run(updateManager.CheckForUpdateAsync).ContinueWith(task => Schedule(() =>
|
|
||||||
{
|
|
||||||
if (!task.GetResultSafely())
|
|
||||||
{
|
|
||||||
notifications?.Post(new SimpleNotification
|
|
||||||
{
|
|
||||||
Text = GeneralSettingsStrings.RunningLatestRelease(game!.Version),
|
|
||||||
Icon = FontAwesome.Solid.CheckCircle,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForUpdatesButton.Enabled.Value = true;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +81,44 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task checkForUpdates()
|
||||||
|
{
|
||||||
|
if (updateManager == null || game == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
checkForUpdatesButton.Enabled.Value = false;
|
||||||
|
|
||||||
|
var checkingNotification = new ProgressNotification
|
||||||
|
{
|
||||||
|
Text = GeneralSettingsStrings.CheckingForUpdates,
|
||||||
|
};
|
||||||
|
notifications?.Post(checkingNotification);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool foundUpdate = await updateManager.CheckForUpdateAsync().ConfigureAwait(true);
|
||||||
|
|
||||||
|
if (!foundUpdate)
|
||||||
|
{
|
||||||
|
notifications?.Post(new SimpleNotification
|
||||||
|
{
|
||||||
|
Text = GeneralSettingsStrings.RunningLatestRelease(game.Version),
|
||||||
|
Icon = FontAwesome.Solid.CheckCircle,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// This sequence allows the notification to be immediately dismissed.
|
||||||
|
checkingNotification.State = ProgressNotificationState.Cancelled;
|
||||||
|
checkingNotification.Close(false);
|
||||||
|
checkForUpdatesButton.Enabled.Value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void exportLogs()
|
private void exportLogs()
|
||||||
{
|
{
|
||||||
ProgressNotification notification = new ProgressNotification
|
ProgressNotification notification = new ProgressNotification
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
[Resolved(canBeNull: true)]
|
[Resolved(canBeNull: true)]
|
||||||
private OsuGame game { get; set; }
|
private OsuGame game { get; set; }
|
||||||
|
|
||||||
public override bool AllowBackButton => false;
|
public override bool AllowUserExit => false;
|
||||||
|
|
||||||
public override bool AllowExternalScreenChange => false;
|
public override bool AllowExternalScreenChange => false;
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ namespace osu.Game.Overlays
|
|||||||
content.ResizeHeightTo(0, animate ? transition_duration : 0, Easing.OutQuint);
|
content.ResizeHeightTo(0, animate ? transition_duration : 0, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
headerContent.FadeColour(Expanded.Value ? Color4.White : OsuColour.Gray(0.5f), 200, Easing.OutQuint);
|
headerContent.FadeColour(Expanded.Value ? Color4.White : OsuColour.Gray(0.7f), 200, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFadeState()
|
private void updateFadeState()
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user