Merge remote-tracking branch 'upstream/master' into back-button-part-2
48
Gemfile.lock
@ -6,7 +6,7 @@ GEM
|
|||||||
public_suffix (>= 2.0.2, < 4.0)
|
public_suffix (>= 2.0.2, < 4.0)
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
babosa (1.0.2)
|
babosa (1.0.2)
|
||||||
claide (1.0.2)
|
claide (1.0.3)
|
||||||
colored (1.2)
|
colored (1.2)
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
commander-fastlane (4.4.6)
|
commander-fastlane (4.4.6)
|
||||||
@ -14,11 +14,11 @@ GEM
|
|||||||
declarative (0.0.10)
|
declarative (0.0.10)
|
||||||
declarative-option (0.1.0)
|
declarative-option (0.1.0)
|
||||||
digest-crc (0.4.1)
|
digest-crc (0.4.1)
|
||||||
domain_name (0.5.20180417)
|
domain_name (0.5.20190701)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
dotenv (2.7.1)
|
dotenv (2.7.5)
|
||||||
emoji_regex (1.0.1)
|
emoji_regex (1.0.1)
|
||||||
excon (0.62.0)
|
excon (0.66.0)
|
||||||
faraday (0.15.4)
|
faraday (0.15.4)
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
faraday-cookie_jar (0.0.6)
|
faraday-cookie_jar (0.0.6)
|
||||||
@ -27,7 +27,7 @@ GEM
|
|||||||
faraday_middleware (0.13.1)
|
faraday_middleware (0.13.1)
|
||||||
faraday (>= 0.7.4, < 1.0)
|
faraday (>= 0.7.4, < 1.0)
|
||||||
fastimage (2.1.5)
|
fastimage (2.1.5)
|
||||||
fastlane (2.117.0)
|
fastlane (2.129.0)
|
||||||
CFPropertyList (>= 2.3, < 4.0.0)
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
addressable (>= 2.3, < 3.0.0)
|
addressable (>= 2.3, < 3.0.0)
|
||||||
babosa (>= 1.0.2, < 2.0.0)
|
babosa (>= 1.0.2, < 2.0.0)
|
||||||
@ -46,8 +46,8 @@ GEM
|
|||||||
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||||
highline (>= 1.7.2, < 2.0.0)
|
highline (>= 1.7.2, < 2.0.0)
|
||||||
json (< 3.0.0)
|
json (< 3.0.0)
|
||||||
mini_magick (~> 4.5.1)
|
jwt (~> 2.1.0)
|
||||||
multi_json
|
mini_magick (>= 4.9.4, < 5.0.0)
|
||||||
multi_xml (~> 0.5)
|
multi_xml (~> 0.5)
|
||||||
multipart-post (~> 2.0.0)
|
multipart-post (~> 2.0.0)
|
||||||
plist (>= 3.1.0, < 4.0.0)
|
plist (>= 3.1.0, < 4.0.0)
|
||||||
@ -56,15 +56,15 @@ GEM
|
|||||||
security (= 0.1.3)
|
security (= 0.1.3)
|
||||||
simctl (~> 1.6.3)
|
simctl (~> 1.6.3)
|
||||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||||
terminal-notifier (>= 1.6.2, < 2.0.0)
|
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||||
terminal-table (>= 1.4.5, < 2.0.0)
|
terminal-table (>= 1.4.5, < 2.0.0)
|
||||||
tty-screen (>= 0.6.3, < 1.0.0)
|
tty-screen (>= 0.6.3, < 1.0.0)
|
||||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||||
word_wrap (~> 1.0.0)
|
word_wrap (~> 1.0.0)
|
||||||
xcodeproj (>= 1.6.0, < 2.0.0)
|
xcodeproj (>= 1.8.1, < 2.0.0)
|
||||||
xcpretty (~> 0.3.0)
|
xcpretty (~> 0.3.0)
|
||||||
xcpretty-travis-formatter (>= 0.0.3)
|
xcpretty-travis-formatter (>= 0.0.3)
|
||||||
fastlane-plugin-clean_testflight_testers (0.2.0)
|
fastlane-plugin-clean_testflight_testers (0.3.0)
|
||||||
fastlane-plugin-souyuz (0.8.1)
|
fastlane-plugin-souyuz (0.8.1)
|
||||||
souyuz (>= 0.8.1)
|
souyuz (>= 0.8.1)
|
||||||
fastlane-plugin-xamarin (0.6.3)
|
fastlane-plugin-xamarin (0.6.3)
|
||||||
@ -79,7 +79,7 @@ GEM
|
|||||||
signet (~> 0.9)
|
signet (~> 0.9)
|
||||||
google-cloud-core (1.3.0)
|
google-cloud-core (1.3.0)
|
||||||
google-cloud-env (~> 1.0)
|
google-cloud-env (~> 1.0)
|
||||||
google-cloud-env (1.0.5)
|
google-cloud-env (1.2.0)
|
||||||
faraday (~> 0.11)
|
faraday (~> 0.11)
|
||||||
google-cloud-storage (1.16.0)
|
google-cloud-storage (1.16.0)
|
||||||
digest-crc (~> 0.4)
|
digest-crc (~> 0.4)
|
||||||
@ -102,17 +102,17 @@ GEM
|
|||||||
memoist (0.16.0)
|
memoist (0.16.0)
|
||||||
mime-types (3.2.2)
|
mime-types (3.2.2)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2018.0812)
|
mime-types-data (3.2019.0331)
|
||||||
mini_magick (4.5.1)
|
mini_magick (4.9.5)
|
||||||
mini_portile2 (2.4.0)
|
mini_portile2 (2.4.0)
|
||||||
multi_json (1.13.1)
|
multi_json (1.13.1)
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
multipart-post (2.0.0)
|
multipart-post (2.0.0)
|
||||||
nanaimo (0.2.6)
|
nanaimo (0.2.6)
|
||||||
naturally (2.2.0)
|
naturally (2.2.0)
|
||||||
nokogiri (1.10.1)
|
nokogiri (1.10.4)
|
||||||
mini_portile2 (~> 2.4.0)
|
mini_portile2 (~> 2.4.0)
|
||||||
os (1.0.0)
|
os (1.0.1)
|
||||||
plist (3.5.0)
|
plist (3.5.0)
|
||||||
public_suffix (2.0.5)
|
public_suffix (2.0.5)
|
||||||
representable (3.0.4)
|
representable (3.0.4)
|
||||||
@ -121,7 +121,7 @@ GEM
|
|||||||
uber (< 0.2.0)
|
uber (< 0.2.0)
|
||||||
retriable (3.1.2)
|
retriable (3.1.2)
|
||||||
rouge (2.0.7)
|
rouge (2.0.7)
|
||||||
rubyzip (1.2.2)
|
rubyzip (1.2.3)
|
||||||
security (0.1.3)
|
security (0.1.3)
|
||||||
signet (0.11.0)
|
signet (0.11.0)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
@ -136,20 +136,20 @@ GEM
|
|||||||
fastlane (>= 2.29.0)
|
fastlane (>= 2.29.0)
|
||||||
highline (~> 1.7)
|
highline (~> 1.7)
|
||||||
nokogiri (~> 1.7)
|
nokogiri (~> 1.7)
|
||||||
terminal-notifier (1.8.0)
|
terminal-notifier (2.0.0)
|
||||||
terminal-table (1.8.0)
|
terminal-table (1.8.0)
|
||||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
tty-cursor (0.6.1)
|
tty-cursor (0.7.0)
|
||||||
tty-screen (0.6.5)
|
tty-screen (0.7.0)
|
||||||
tty-spinner (0.9.0)
|
tty-spinner (0.9.1)
|
||||||
tty-cursor (~> 0.6.0)
|
tty-cursor (~> 0.7)
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.7.5)
|
unf_ext (0.0.7.6)
|
||||||
unicode-display_width (1.4.1)
|
unicode-display_width (1.6.0)
|
||||||
word_wrap (1.0.0)
|
word_wrap (1.0.0)
|
||||||
xcodeproj (1.8.1)
|
xcodeproj (1.12.0)
|
||||||
CFPropertyList (>= 2.3.3, < 4.0)
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
atomos (~> 0.1.3)
|
atomos (~> 0.1.3)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
|
@ -1,22 +1,6 @@
|
|||||||
update_fastlane
|
update_fastlane
|
||||||
|
|
||||||
default_platform(:ios)
|
|
||||||
|
|
||||||
platform :ios do
|
platform :ios do
|
||||||
lane :testflight_prune_dry do
|
|
||||||
clean_testflight_testers(days_of_inactivity:45, dry_run: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Specify a custom number for what's "inactive"
|
|
||||||
lane :testflight_prune do
|
|
||||||
clean_testflight_testers(days_of_inactivity: 45) # 120 days, so about 4 months
|
|
||||||
end
|
|
||||||
|
|
||||||
lane :update_version do |options|
|
|
||||||
options[:plist_path] = '../osu.iOS/Info.plist'
|
|
||||||
app_version(options)
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Deploy to testflight'
|
desc 'Deploy to testflight'
|
||||||
lane :beta do |options|
|
lane :beta do |options|
|
||||||
update_version(options)
|
update_version(options)
|
||||||
@ -62,4 +46,17 @@ platform :ios do
|
|||||||
|
|
||||||
match(options)
|
match(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
lane :update_version do |options|
|
||||||
|
options[:plist_path] = '../osu.iOS/Info.plist'
|
||||||
|
app_version(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
lane :testflight_prune_dry do
|
||||||
|
clean_testflight_testers(days_of_inactivity:45, dry_run: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
lane :testflight_prune do
|
||||||
|
clean_testflight_testers(days_of_inactivity: 45)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -16,21 +16,6 @@ or alternatively using `brew cask install fastlane`
|
|||||||
|
|
||||||
# Available Actions
|
# Available Actions
|
||||||
## iOS
|
## iOS
|
||||||
### ios testflight_prune_dry
|
|
||||||
```
|
|
||||||
fastlane ios testflight_prune_dry
|
|
||||||
```
|
|
||||||
|
|
||||||
### ios testflight_prune
|
|
||||||
```
|
|
||||||
fastlane ios testflight_prune
|
|
||||||
```
|
|
||||||
|
|
||||||
### ios update_version
|
|
||||||
```
|
|
||||||
fastlane ios update_version
|
|
||||||
```
|
|
||||||
|
|
||||||
### ios beta
|
### ios beta
|
||||||
```
|
```
|
||||||
fastlane ios beta
|
fastlane ios beta
|
||||||
@ -46,6 +31,21 @@ Compile the project
|
|||||||
fastlane ios provision
|
fastlane ios provision
|
||||||
```
|
```
|
||||||
Install provisioning profiles using match
|
Install provisioning profiles using match
|
||||||
|
### ios update_version
|
||||||
|
```
|
||||||
|
fastlane ios update_version
|
||||||
|
```
|
||||||
|
|
||||||
|
### ios testflight_prune_dry
|
||||||
|
```
|
||||||
|
fastlane ios testflight_prune_dry
|
||||||
|
```
|
||||||
|
|
||||||
|
### ios testflight_prune
|
||||||
|
```
|
||||||
|
fastlane ios testflight_prune
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -61,6 +61,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.813.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.816.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
BIN
osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit0@2x.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit100@2x.png
Normal file
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 21 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300@2x.png
Normal file
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 39 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit50@2x.png
Normal file
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit0@2x.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit100@2x.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit300@2x.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/metrics-skin/hit50@2x.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit0-0@2x.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hit0-1@2x.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 8.9 KiB |
@ -2,12 +2,12 @@
|
|||||||
// 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.IO;
|
using System.Text.RegularExpressions;
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
@ -28,11 +28,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
var skins = new SkinManager(LocalStorage, ContextFactory, null, audio);
|
var dllStore = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll");
|
||||||
|
|
||||||
metricsSkin = getSkinFromResources(skins, "metrics_skin");
|
metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
|
||||||
defaultSkin = getSkinFromResources(skins, "default_skin");
|
defaultSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/default_skin"), audio, false);
|
||||||
specialSkin = getSkinFromResources(skins, "special_skin");
|
specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/special_skin"), audio, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetContents(Func<Drawable> creationFunction)
|
public void SetContents(Func<Drawable> creationFunction)
|
||||||
@ -43,23 +43,28 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
Cell(3).Child = new LocalSkinOverrideContainer(specialSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction());
|
Cell(3).Child = new LocalSkinOverrideContainer(specialSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Skin getSkinFromResources(SkinManager skins, string name)
|
private class TestLegacySkin : LegacySkin
|
||||||
{
|
{
|
||||||
using (var storage = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll"))
|
private readonly bool extrapolateAnimations;
|
||||||
|
|
||||||
|
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager, bool extrapolateAnimations)
|
||||||
|
: base(skin, storage, audioManager, "skin.ini")
|
||||||
{
|
{
|
||||||
var tempName = Path.GetTempFileName();
|
this.extrapolateAnimations = extrapolateAnimations;
|
||||||
|
}
|
||||||
|
|
||||||
File.Delete(tempName);
|
public override Texture GetTexture(string componentName)
|
||||||
Directory.CreateDirectory(tempName);
|
{
|
||||||
|
// extrapolate frames to test longer animations
|
||||||
|
if (extrapolateAnimations)
|
||||||
|
{
|
||||||
|
var match = Regex.Match(componentName, "-([0-9]*)");
|
||||||
|
|
||||||
var files = storage.GetAvailableResources().Where(f => f.StartsWith($"Resources/{name}"));
|
if (match.Length > 0 && int.TryParse(match.Groups[1].Value, out var number) && number < 60)
|
||||||
|
return base.GetTexture(componentName.Replace($"-{number}", $"-{number % 2}"));
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var file in files)
|
return base.GetTexture(componentName);
|
||||||
using (var stream = storage.GetStream(file))
|
|
||||||
using (var newFile = File.Create(Path.Combine(tempName, Path.GetFileName(file))))
|
|
||||||
stream.CopyTo(newFile);
|
|
||||||
|
|
||||||
return skins.GetSkin(skins.Import(tempName).Result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
34
osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// 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.Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneDrawableJudgement : SkinnableTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(DrawableJudgement),
|
||||||
|
typeof(DrawableOsuJudgement)
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestSceneDrawableJudgement()
|
||||||
|
{
|
||||||
|
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
|
||||||
|
AddStep("Show " + result.GetDescription(), () => SetContents(() =>
|
||||||
|
new DrawableOsuJudgement(new JudgementResult(null) { Type = result }, null)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,6 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -27,83 +26,96 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
|||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneSlider : OsuTestScene
|
public class TestSceneSlider : SkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
|
typeof(Slider),
|
||||||
|
typeof(SliderTick),
|
||||||
|
typeof(SliderTailCircle),
|
||||||
typeof(SliderBall),
|
typeof(SliderBall),
|
||||||
typeof(SliderBody),
|
typeof(SliderBody),
|
||||||
typeof(SliderTick),
|
typeof(SnakingSliderBody),
|
||||||
typeof(DrawableSlider),
|
typeof(DrawableSlider),
|
||||||
typeof(DrawableSliderTick),
|
typeof(DrawableSliderTick),
|
||||||
|
typeof(DrawableSliderTail),
|
||||||
|
typeof(DrawableSliderHead),
|
||||||
typeof(DrawableRepeatPoint),
|
typeof(DrawableRepeatPoint),
|
||||||
typeof(DrawableOsuHitObject)
|
typeof(DrawableOsuHitObject)
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly Container content;
|
private Container content;
|
||||||
protected override Container<Drawable> Content => content;
|
|
||||||
|
protected override Container<Drawable> Content
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (content == null)
|
||||||
|
base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int depthIndex;
|
private int depthIndex;
|
||||||
|
|
||||||
public TestSceneSlider()
|
public TestSceneSlider()
|
||||||
{
|
{
|
||||||
base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
|
AddStep("Big Single", () => SetContents(() => testSimpleBig()));
|
||||||
|
AddStep("Medium Single", () => SetContents(() => testSimpleMedium()));
|
||||||
|
AddStep("Small Single", () => SetContents(() => testSimpleSmall()));
|
||||||
|
AddStep("Big 1 Repeat", () => SetContents(() => testSimpleBig(1)));
|
||||||
|
AddStep("Medium 1 Repeat", () => SetContents(() => testSimpleMedium(1)));
|
||||||
|
AddStep("Small 1 Repeat", () => SetContents(() => testSimpleSmall(1)));
|
||||||
|
AddStep("Big 2 Repeats", () => SetContents(() => testSimpleBig(2)));
|
||||||
|
AddStep("Medium 2 Repeats", () => SetContents(() => testSimpleMedium(2)));
|
||||||
|
AddStep("Small 2 Repeats", () => SetContents(() => testSimpleSmall(2)));
|
||||||
|
|
||||||
AddStep("Big Single", () => testSimpleBig());
|
AddStep("Slow Slider", () => SetContents(testSlowSpeed)); // slow long sliders take ages already so no repeat steps
|
||||||
AddStep("Medium Single", () => testSimpleMedium());
|
AddStep("Slow Short Slider", () => SetContents(() => testShortSlowSpeed()));
|
||||||
AddStep("Small Single", () => testSimpleSmall());
|
AddStep("Slow Short Slider 1 Repeats", () => SetContents(() => testShortSlowSpeed(1)));
|
||||||
AddStep("Big 1 Repeat", () => testSimpleBig(1));
|
AddStep("Slow Short Slider 2 Repeats", () => SetContents(() => testShortSlowSpeed(2)));
|
||||||
AddStep("Medium 1 Repeat", () => testSimpleMedium(1));
|
|
||||||
AddStep("Small 1 Repeat", () => testSimpleSmall(1));
|
|
||||||
AddStep("Big 2 Repeats", () => testSimpleBig(2));
|
|
||||||
AddStep("Medium 2 Repeats", () => testSimpleMedium(2));
|
|
||||||
AddStep("Small 2 Repeats", () => testSimpleSmall(2));
|
|
||||||
|
|
||||||
AddStep("Slow Slider", testSlowSpeed); // slow long sliders take ages already so no repeat steps
|
AddStep("Fast Slider", () => SetContents(() => testHighSpeed()));
|
||||||
AddStep("Slow Short Slider", () => testShortSlowSpeed());
|
AddStep("Fast Slider 1 Repeat", () => SetContents(() => testHighSpeed(1)));
|
||||||
AddStep("Slow Short Slider 1 Repeats", () => testShortSlowSpeed(1));
|
AddStep("Fast Slider 2 Repeats", () => SetContents(() => testHighSpeed(2)));
|
||||||
AddStep("Slow Short Slider 2 Repeats", () => testShortSlowSpeed(2));
|
AddStep("Fast Short Slider", () => SetContents(() => testShortHighSpeed()));
|
||||||
|
AddStep("Fast Short Slider 1 Repeat", () => SetContents(() => testShortHighSpeed(1)));
|
||||||
|
AddStep("Fast Short Slider 2 Repeats", () => SetContents(() => testShortHighSpeed(2)));
|
||||||
|
AddStep("Fast Short Slider 6 Repeats", () => SetContents(() => testShortHighSpeed(6)));
|
||||||
|
|
||||||
AddStep("Fast Slider", () => testHighSpeed());
|
AddStep("Perfect Curve", () => SetContents(() => testPerfect()));
|
||||||
AddStep("Fast Slider 1 Repeat", () => testHighSpeed(1));
|
AddStep("Perfect Curve 1 Repeat", () => SetContents(() => testPerfect(1)));
|
||||||
AddStep("Fast Slider 2 Repeats", () => testHighSpeed(2));
|
AddStep("Perfect Curve 2 Repeats", () => SetContents(() => testPerfect(2)));
|
||||||
AddStep("Fast Short Slider", () => testShortHighSpeed());
|
|
||||||
AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1));
|
|
||||||
AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2));
|
|
||||||
AddStep("Fast Short Slider 6 Repeats", () => testShortHighSpeed(6));
|
|
||||||
|
|
||||||
AddStep("Perfect Curve", () => testPerfect());
|
AddStep("Linear Slider", () => SetContents(() => testLinear()));
|
||||||
AddStep("Perfect Curve 1 Repeat", () => testPerfect(1));
|
AddStep("Linear Slider 1 Repeat", () => SetContents(() => testLinear(1)));
|
||||||
AddStep("Perfect Curve 2 Repeats", () => testPerfect(2));
|
AddStep("Linear Slider 2 Repeats", () => SetContents(() => testLinear(2)));
|
||||||
|
|
||||||
AddStep("Linear Slider", () => testLinear());
|
AddStep("Bezier Slider", () => SetContents(() => testBezier()));
|
||||||
AddStep("Linear Slider 1 Repeat", () => testLinear(1));
|
AddStep("Bezier Slider 1 Repeat", () => SetContents(() => testBezier(1)));
|
||||||
AddStep("Linear Slider 2 Repeats", () => testLinear(2));
|
AddStep("Bezier Slider 2 Repeats", () => SetContents(() => testBezier(2)));
|
||||||
|
|
||||||
AddStep("Bezier Slider", () => testBezier());
|
AddStep("Linear Overlapping", () => SetContents(() => testLinearOverlapping()));
|
||||||
AddStep("Bezier Slider 1 Repeat", () => testBezier(1));
|
AddStep("Linear Overlapping 1 Repeat", () => SetContents(() => testLinearOverlapping(1)));
|
||||||
AddStep("Bezier Slider 2 Repeats", () => testBezier(2));
|
AddStep("Linear Overlapping 2 Repeats", () => SetContents(() => testLinearOverlapping(2)));
|
||||||
|
|
||||||
AddStep("Linear Overlapping", () => testLinearOverlapping());
|
AddStep("Catmull Slider", () => SetContents(() => testCatmull()));
|
||||||
AddStep("Linear Overlapping 1 Repeat", () => testLinearOverlapping(1));
|
AddStep("Catmull Slider 1 Repeat", () => SetContents(() => testCatmull(1)));
|
||||||
AddStep("Linear Overlapping 2 Repeats", () => testLinearOverlapping(2));
|
AddStep("Catmull Slider 2 Repeats", () => SetContents(() => testCatmull(2)));
|
||||||
|
|
||||||
AddStep("Catmull Slider", () => testCatmull());
|
AddStep("Big Single, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset()));
|
||||||
AddStep("Catmull Slider 1 Repeat", () => testCatmull(1));
|
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset(1)));
|
||||||
AddStep("Catmull Slider 2 Repeats", () => testCatmull(2));
|
|
||||||
|
|
||||||
AddStep("Big Single, Large StackOffset", () => testSimpleBigLargeStackOffset());
|
AddStep("Distance Overflow", () => SetContents(() => testDistanceOverflow()));
|
||||||
AddStep("Big 1 Repeat, Large StackOffset", () => testSimpleBigLargeStackOffset(1));
|
AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1)));
|
||||||
|
|
||||||
AddStep("Distance Overflow", () => testDistanceOverflow());
|
|
||||||
AddStep("Distance Overflow 1 Repeat", () => testDistanceOverflow(1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
private Drawable testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
||||||
|
|
||||||
private void testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10);
|
private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10);
|
||||||
|
|
||||||
private void testDistanceOverflow(int repeats = 0)
|
private Drawable testDistanceOverflow(int repeats = 0)
|
||||||
{
|
{
|
||||||
var slider = new Slider
|
var slider = new Slider
|
||||||
{
|
{
|
||||||
@ -120,22 +132,22 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
StackHeight = 10
|
StackHeight = 10
|
||||||
};
|
};
|
||||||
|
|
||||||
addSlider(slider, 2, 2);
|
return createDrawable(slider, 2, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats);
|
private Drawable testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats);
|
||||||
|
|
||||||
private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats);
|
private Drawable testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats);
|
||||||
|
|
||||||
private void testSlowSpeed() => createSlider(speedMultiplier: 0.5);
|
private Drawable testSlowSpeed() => createSlider(speedMultiplier: 0.5);
|
||||||
|
|
||||||
private void testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5);
|
private Drawable testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5);
|
||||||
|
|
||||||
private void testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15);
|
private Drawable testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15);
|
||||||
|
|
||||||
private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15);
|
private Drawable testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15);
|
||||||
|
|
||||||
private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0)
|
private Drawable createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0)
|
||||||
{
|
{
|
||||||
var slider = new Slider
|
var slider = new Slider
|
||||||
{
|
{
|
||||||
@ -151,10 +163,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
StackHeight = stackHeight
|
StackHeight = stackHeight
|
||||||
};
|
};
|
||||||
|
|
||||||
addSlider(slider, circleSize, speedMultiplier);
|
return createDrawable(slider, circleSize, speedMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testPerfect(int repeats = 0)
|
private Drawable testPerfect(int repeats = 0)
|
||||||
{
|
{
|
||||||
var slider = new Slider
|
var slider = new Slider
|
||||||
{
|
{
|
||||||
@ -170,12 +182,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
NodeSamples = createEmptySamples(repeats)
|
NodeSamples = createEmptySamples(repeats)
|
||||||
};
|
};
|
||||||
|
|
||||||
addSlider(slider, 2, 3);
|
return createDrawable(slider, 2, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testLinear(int repeats = 0) => createLinear(repeats);
|
private Drawable testLinear(int repeats = 0) => createLinear(repeats);
|
||||||
|
|
||||||
private void createLinear(int repeats)
|
private Drawable createLinear(int repeats)
|
||||||
{
|
{
|
||||||
var slider = new Slider
|
var slider = new Slider
|
||||||
{
|
{
|
||||||
@ -194,12 +206,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
NodeSamples = createEmptySamples(repeats)
|
NodeSamples = createEmptySamples(repeats)
|
||||||
};
|
};
|
||||||
|
|
||||||
addSlider(slider, 2, 3);
|
return createDrawable(slider, 2, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testBezier(int repeats = 0) => createBezier(repeats);
|
private Drawable testBezier(int repeats = 0) => createBezier(repeats);
|
||||||
|
|
||||||
private void createBezier(int repeats)
|
private Drawable createBezier(int repeats)
|
||||||
{
|
{
|
||||||
var slider = new Slider
|
var slider = new Slider
|
||||||
{
|
{
|
||||||
@ -217,12 +229,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
NodeSamples = createEmptySamples(repeats)
|
NodeSamples = createEmptySamples(repeats)
|
||||||
};
|
};
|
||||||
|
|
||||||
addSlider(slider, 2, 3);
|
return createDrawable(slider, 2, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testLinearOverlapping(int repeats = 0) => createOverlapping(repeats);
|
private Drawable testLinearOverlapping(int repeats = 0) => createOverlapping(repeats);
|
||||||
|
|
||||||
private void createOverlapping(int repeats)
|
private Drawable createOverlapping(int repeats)
|
||||||
{
|
{
|
||||||
var slider = new Slider
|
var slider = new Slider
|
||||||
{
|
{
|
||||||
@ -241,12 +253,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
NodeSamples = createEmptySamples(repeats)
|
NodeSamples = createEmptySamples(repeats)
|
||||||
};
|
};
|
||||||
|
|
||||||
addSlider(slider, 2, 3);
|
return createDrawable(slider, 2, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testCatmull(int repeats = 0) => createCatmull(repeats);
|
private Drawable testCatmull(int repeats = 0) => createCatmull(repeats);
|
||||||
|
|
||||||
private void createCatmull(int repeats = 0)
|
private Drawable createCatmull(int repeats = 0)
|
||||||
{
|
{
|
||||||
var repeatSamples = new List<List<HitSampleInfo>>();
|
var repeatSamples = new List<List<HitSampleInfo>>();
|
||||||
for (int i = 0; i < repeats; i++)
|
for (int i = 0; i < repeats; i++)
|
||||||
@ -267,7 +279,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
NodeSamples = repeatSamples
|
NodeSamples = repeatSamples
|
||||||
};
|
};
|
||||||
|
|
||||||
addSlider(slider, 3, 1);
|
return createDrawable(slider, 3, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<List<HitSampleInfo>> createEmptySamples(int repeats)
|
private List<List<HitSampleInfo>> createEmptySamples(int repeats)
|
||||||
@ -278,7 +290,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
return repeatSamples;
|
return repeatSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSlider(Slider slider, float circleSize, double speedMultiplier)
|
private Drawable createDrawable(Slider slider, float circleSize, double speedMultiplier)
|
||||||
{
|
{
|
||||||
var cpi = new ControlPointInfo();
|
var cpi = new ControlPointInfo();
|
||||||
cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier });
|
cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier });
|
||||||
@ -296,7 +308,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
drawable.OnNewResult += onNewResult;
|
drawable.OnNewResult += onNewResult;
|
||||||
|
|
||||||
Add(drawable);
|
return drawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float judgementOffsetDirection = 1;
|
private float judgementOffsetDirection = 1;
|
||||||
|
@ -55,7 +55,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
Child = new Container
|
Child = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
// TODO: support skin filename animation (sliderb0, sliderb1...)
|
|
||||||
Child = new SkinnableDrawable("Play/osu/sliderball", _ => new DefaultSliderBall()),
|
Child = new SkinnableDrawable("Play/osu/sliderball", _ => new DefaultSliderBall()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,9 +167,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
return action == OsuAction.LeftButton || action == OsuAction.RightButton;
|
return action == OsuAction.LeftButton || action == OsuAction.RightButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Vector2? lastPosition;
|
||||||
|
|
||||||
public void UpdateProgress(double completionProgress)
|
public void UpdateProgress(double completionProgress)
|
||||||
{
|
{
|
||||||
Position = slider.CurvePositionAt(completionProgress);
|
var newPos = slider.CurvePositionAt(completionProgress);
|
||||||
|
|
||||||
|
var diff = lastPosition.HasValue ? lastPosition.Value - newPos : newPos - slider.CurvePositionAt(completionProgress + 0.01f);
|
||||||
|
if (diff == Vector2.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Position = newPos;
|
||||||
|
Rotation = -90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI);
|
||||||
|
|
||||||
|
lastPosition = newPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FollowCircleContainer : Container
|
private class FollowCircleContainer : Container
|
||||||
|
@ -17,6 +17,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
private bool exitAction;
|
private bool exitAction;
|
||||||
|
|
||||||
|
protected override double TimePerAction => 100; // required for the early exit test, since hold-to-confirm delay is 200ms
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
|
|
||||||
@ -12,7 +13,13 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
public class TestSceneHoldToConfirmOverlay : OsuTestScene
|
public class TestSceneHoldToConfirmOverlay : OsuTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(ExitConfirmOverlay) };
|
protected override double TimePerAction => 100; // required for the early exit test, since hold-to-confirm delay is 200ms
|
||||||
|
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(ExitConfirmOverlay),
|
||||||
|
typeof(HoldToConfirmContainer),
|
||||||
|
};
|
||||||
|
|
||||||
public TestSceneHoldToConfirmOverlay()
|
public TestSceneHoldToConfirmOverlay()
|
||||||
{
|
{
|
||||||
|
@ -129,6 +129,23 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ScoreInfo> Scores { get; set; }
|
public List<ScoreInfo> Scores { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public DifficultyRating DifficultyRating
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var rating = StarDifficulty;
|
||||||
|
|
||||||
|
if (rating < 2.0) return DifficultyRating.Easy;
|
||||||
|
if (rating < 2.7) return DifficultyRating.Normal;
|
||||||
|
if (rating < 4.0) return DifficultyRating.Hard;
|
||||||
|
if (rating < 5.3) return DifficultyRating.Insane;
|
||||||
|
if (rating < 6.5) return DifficultyRating.Expert;
|
||||||
|
|
||||||
|
return DifficultyRating.ExpertPlus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString() => $"{Metadata} [{Version}]".Trim();
|
public override string ToString() => $"{Metadata} [{Version}]".Trim();
|
||||||
|
|
||||||
public bool Equals(BeatmapInfo other)
|
public bool Equals(BeatmapInfo other)
|
||||||
|
15
osu.Game/Beatmaps/DifficultyRating.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps
|
||||||
|
{
|
||||||
|
public enum DifficultyRating
|
||||||
|
{
|
||||||
|
Easy,
|
||||||
|
Normal,
|
||||||
|
Hard,
|
||||||
|
Insane,
|
||||||
|
Expert,
|
||||||
|
ExpertPlus
|
||||||
|
}
|
||||||
|
}
|
@ -1,85 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables
|
|
||||||
{
|
|
||||||
public abstract class DifficultyColouredContainer : Container, IHasAccentColour
|
|
||||||
{
|
|
||||||
public Color4 AccentColour { get; set; }
|
|
||||||
|
|
||||||
private readonly BeatmapInfo beatmap;
|
|
||||||
private OsuColour palette;
|
|
||||||
|
|
||||||
protected DifficultyColouredContainer(BeatmapInfo beatmap)
|
|
||||||
{
|
|
||||||
this.beatmap = beatmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour palette)
|
|
||||||
{
|
|
||||||
if (palette == null)
|
|
||||||
throw new ArgumentNullException(nameof(palette));
|
|
||||||
|
|
||||||
this.palette = palette;
|
|
||||||
AccentColour = getColour(beatmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum DifficultyRating
|
|
||||||
{
|
|
||||||
Easy,
|
|
||||||
Normal,
|
|
||||||
Hard,
|
|
||||||
Insane,
|
|
||||||
Expert,
|
|
||||||
ExpertPlus
|
|
||||||
}
|
|
||||||
|
|
||||||
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
|
|
||||||
{
|
|
||||||
if (beatmap == null)
|
|
||||||
throw new ArgumentNullException(nameof(beatmap));
|
|
||||||
|
|
||||||
var rating = beatmap.StarDifficulty;
|
|
||||||
|
|
||||||
if (rating < 2.0) return DifficultyRating.Easy;
|
|
||||||
if (rating < 2.7) return DifficultyRating.Normal;
|
|
||||||
if (rating < 4.0) return DifficultyRating.Hard;
|
|
||||||
if (rating < 5.3) return DifficultyRating.Insane;
|
|
||||||
if (rating < 6.5) return DifficultyRating.Expert;
|
|
||||||
|
|
||||||
return DifficultyRating.ExpertPlus;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Color4 getColour(BeatmapInfo beatmap)
|
|
||||||
{
|
|
||||||
switch (getDifficultyRating(beatmap))
|
|
||||||
{
|
|
||||||
case DifficultyRating.Easy:
|
|
||||||
return palette.Green;
|
|
||||||
|
|
||||||
default:
|
|
||||||
case DifficultyRating.Normal:
|
|
||||||
return palette.Blue;
|
|
||||||
|
|
||||||
case DifficultyRating.Hard:
|
|
||||||
return palette.Yellow;
|
|
||||||
|
|
||||||
case DifficultyRating.Insane:
|
|
||||||
return palette.Pink;
|
|
||||||
|
|
||||||
case DifficultyRating.Expert:
|
|
||||||
return palette.Purple;
|
|
||||||
|
|
||||||
case DifficultyRating.ExpertPlus:
|
|
||||||
return palette.Gray0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -6,33 +6,46 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables
|
namespace osu.Game.Beatmaps.Drawables
|
||||||
{
|
{
|
||||||
public class DifficultyIcon : DifficultyColouredContainer
|
public class DifficultyIcon : Container, IHasCustomTooltip
|
||||||
{
|
{
|
||||||
|
private readonly BeatmapInfo beatmap;
|
||||||
private readonly RulesetInfo ruleset;
|
private readonly RulesetInfo ruleset;
|
||||||
|
|
||||||
public DifficultyIcon(BeatmapInfo beatmap, RulesetInfo ruleset = null)
|
public DifficultyIcon(BeatmapInfo beatmap, RulesetInfo ruleset = null, bool shouldShowTooltip = true)
|
||||||
: base(beatmap)
|
|
||||||
{
|
{
|
||||||
if (beatmap == null)
|
if (beatmap == null)
|
||||||
throw new ArgumentNullException(nameof(beatmap));
|
throw new ArgumentNullException(nameof(beatmap));
|
||||||
|
|
||||||
|
this.beatmap = beatmap;
|
||||||
|
|
||||||
this.ruleset = ruleset ?? beatmap.Ruleset;
|
this.ruleset = ruleset ?? beatmap.Ruleset;
|
||||||
|
if (shouldShowTooltip)
|
||||||
|
TooltipContent = beatmap;
|
||||||
|
|
||||||
Size = new Vector2(20);
|
Size = new Vector2(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string TooltipText { get; set; }
|
||||||
|
|
||||||
|
public ITooltip GetCustomTooltip() => new DifficultyIconTooltip();
|
||||||
|
|
||||||
|
public object TooltipContent { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -52,7 +65,7 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
Child = new Box
|
Child = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = AccentColour,
|
Colour = colours.ForDifficultyRating(beatmap.DifficultyRating),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ConstrainedIconContainer
|
new ConstrainedIconContainer
|
||||||
@ -65,5 +78,105 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DifficultyIconTooltip : VisibilityContainer, ITooltip
|
||||||
|
{
|
||||||
|
private readonly OsuSpriteText difficultyName, starRating;
|
||||||
|
private readonly Box background;
|
||||||
|
|
||||||
|
private readonly FillFlowContainer difficultyFlow;
|
||||||
|
|
||||||
|
public string TooltipText
|
||||||
|
{
|
||||||
|
set { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public DifficultyIconTooltip()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 5;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
AutoSizeDuration = 200,
|
||||||
|
AutoSizeEasing = Easing.OutQuint,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Padding = new MarginPadding(10),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
difficultyName = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold),
|
||||||
|
},
|
||||||
|
difficultyFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
starRating = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular),
|
||||||
|
},
|
||||||
|
new SpriteIcon
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Margin = new MarginPadding { Left = 4 },
|
||||||
|
Icon = FontAwesome.Solid.Star,
|
||||||
|
Size = new Vector2(12),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private OsuColour colours;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
this.colours = colours;
|
||||||
|
background.Colour = colours.Gray3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetContent(object content)
|
||||||
|
{
|
||||||
|
if (!(content is BeatmapInfo beatmap))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
difficultyName.Text = beatmap.Version;
|
||||||
|
starRating.Text = $"{beatmap.StarDifficulty:0.##}";
|
||||||
|
difficultyFlow.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Move(Vector2 pos) => Position = pos;
|
||||||
|
|
||||||
|
protected override void PopIn() => this.FadeIn(200, Easing.OutQuint);
|
||||||
|
|
||||||
|
protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,11 @@ namespace osu.Game.Graphics.Containers
|
|||||||
{
|
{
|
||||||
public Action Action;
|
public Action Action;
|
||||||
|
|
||||||
private const int activate_delay = 400;
|
private const int default_activation_delay = 200;
|
||||||
private const int fadeout_delay = 200;
|
private const int fadeout_delay = 200;
|
||||||
|
|
||||||
|
private readonly double activationDelay;
|
||||||
|
|
||||||
private bool fired;
|
private bool fired;
|
||||||
private bool confirming;
|
private bool confirming;
|
||||||
|
|
||||||
@ -25,13 +27,22 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
public Bindable<double> Progress = new BindableDouble();
|
public Bindable<double> Progress = new BindableDouble();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="activationDelay">The time requried before an action is confirmed.</param>
|
||||||
|
protected HoldToConfirmContainer(double activationDelay = default_activation_delay)
|
||||||
|
{
|
||||||
|
this.activationDelay = activationDelay;
|
||||||
|
}
|
||||||
|
|
||||||
protected void BeginConfirm()
|
protected void BeginConfirm()
|
||||||
{
|
{
|
||||||
if (confirming || (!AllowMultipleFires && fired)) return;
|
if (confirming || (!AllowMultipleFires && fired)) return;
|
||||||
|
|
||||||
confirming = true;
|
confirming = true;
|
||||||
|
|
||||||
this.TransformBindableTo(Progress, 1, activate_delay * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm());
|
this.TransformBindableTo(Progress, 1, activationDelay * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Confirm()
|
protected virtual void Confirm()
|
||||||
|
@ -30,22 +30,24 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
private readonly OsuSpriteText text;
|
private readonly OsuSpriteText text;
|
||||||
private bool instantMovement = true;
|
private bool instantMovement = true;
|
||||||
|
|
||||||
public override string TooltipText
|
public override bool SetContent(object content)
|
||||||
{
|
{
|
||||||
set
|
if (!(content is string contentString))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (contentString == text.Text) return true;
|
||||||
|
|
||||||
|
text.Text = contentString;
|
||||||
|
|
||||||
|
if (IsPresent)
|
||||||
{
|
{
|
||||||
if (value == text.Text) return;
|
AutoSizeDuration = 250;
|
||||||
|
background.FlashColour(OsuColour.Gray(0.4f), 1000, Easing.OutQuint);
|
||||||
text.Text = value;
|
|
||||||
|
|
||||||
if (IsPresent)
|
|
||||||
{
|
|
||||||
AutoSizeDuration = 250;
|
|
||||||
background.FlashColour(OsuColour.Gray(0.4f), 1000, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
AutoSizeDuration = 0;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
AutoSizeDuration = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsuTooltip()
|
public OsuTooltip()
|
||||||
|
@ -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 osu.Game.Beatmaps;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Graphics
|
namespace osu.Game.Graphics
|
||||||
@ -37,6 +38,31 @@ namespace osu.Game.Graphics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Color4 ForDifficultyRating(DifficultyRating difficulty)
|
||||||
|
{
|
||||||
|
switch (difficulty)
|
||||||
|
{
|
||||||
|
case DifficultyRating.Easy:
|
||||||
|
return Green;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case DifficultyRating.Normal:
|
||||||
|
return Blue;
|
||||||
|
|
||||||
|
case DifficultyRating.Hard:
|
||||||
|
return Yellow;
|
||||||
|
|
||||||
|
case DifficultyRating.Insane:
|
||||||
|
return Pink;
|
||||||
|
|
||||||
|
case DifficultyRating.Expert:
|
||||||
|
return Purple;
|
||||||
|
|
||||||
|
case DifficultyRating.ExpertPlus:
|
||||||
|
return Gray0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less
|
// See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less
|
||||||
public readonly Color4 PurpleLighter = FromHex(@"eeeeff");
|
public readonly Color4 PurpleLighter = FromHex(@"eeeeff");
|
||||||
public readonly Color4 PurpleLight = FromHex(@"aa88ff");
|
public readonly Color4 PurpleLight = FromHex(@"aa88ff");
|
||||||
|
@ -72,17 +72,11 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1;
|
Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
sampleChecked = audio.Samples.Get(@"UI/check-on");
|
||||||
|
sampleUnchecked = audio.Samples.Get(@"UI/check-off");
|
||||||
Current.ValueChanged += enabled =>
|
|
||||||
{
|
|
||||||
if (enabled.NewValue)
|
|
||||||
sampleChecked?.Play();
|
|
||||||
else
|
|
||||||
sampleUnchecked?.Play();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
@ -99,11 +93,13 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
base.OnHoverLost(e);
|
base.OnHoverLost(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
protected override void OnUserChange(bool value)
|
||||||
private void load(AudioManager audio)
|
|
||||||
{
|
{
|
||||||
sampleChecked = audio.Samples.Get(@"UI/check-on");
|
base.OnUserChange(value);
|
||||||
sampleUnchecked = audio.Samples.Get(@"UI/check-off");
|
if (value)
|
||||||
|
sampleChecked?.Play();
|
||||||
|
else
|
||||||
|
sampleUnchecked?.Play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,6 +122,7 @@ namespace osu.Game.Online.Chat
|
|||||||
return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]);
|
return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]);
|
||||||
|
|
||||||
case "u":
|
case "u":
|
||||||
|
case "users":
|
||||||
return new LinkDetails(LinkAction.OpenUserProfile, args[3]);
|
return new LinkDetails(LinkAction.OpenUserProfile, args[3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
Colour = Color4.Black.Opacity(0.5f),
|
Colour = Color4.Black.Opacity(0.5f),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
icon = new DifficultyIcon(beatmap)
|
icon = new DifficultyIcon(beatmap, shouldShowTooltip: false)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -30,27 +30,24 @@ namespace osu.Game.Overlays.Chat
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Child = new OsuContextMenuContainer
|
||||||
{
|
{
|
||||||
scroll = new OsuScrollContainer
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Child = scroll = new OsuScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
// Some chat lines have effects that slightly protrude to the bottom,
|
// Some chat lines have effects that slightly protrude to the bottom,
|
||||||
// which we do not want to mask away, hence the padding.
|
// which we do not want to mask away, hence the padding.
|
||||||
Padding = new MarginPadding { Bottom = 5 },
|
Padding = new MarginPadding { Bottom = 5 },
|
||||||
Child = new OsuContextMenuContainer
|
Child = ChatLineFlow = new ChatLineContainer
|
||||||
{
|
{
|
||||||
|
Padding = new MarginPadding { Left = 20, Right = 20 },
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Child = ChatLineFlow = new ChatLineContainer
|
Direction = FillDirection.Vertical,
|
||||||
{
|
}
|
||||||
Padding = new MarginPadding { Left = 20, Right = 20 },
|
},
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
newMessagesArrived(Channel.Messages);
|
newMessagesArrived(Channel.Messages);
|
||||||
|
@ -92,6 +92,15 @@ namespace osu.Game.Overlays
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start playing the current track (if not already playing).
|
||||||
|
/// </summary>
|
||||||
|
public void Play()
|
||||||
|
{
|
||||||
|
if (!IsPlaying)
|
||||||
|
TogglePause();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Toggle pause / play.
|
/// Toggle pause / play.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -231,6 +240,9 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
public bool OnPressed(GlobalAction action)
|
public bool OnPressed(GlobalAction action)
|
||||||
{
|
{
|
||||||
|
if (beatmap.Disabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case GlobalAction.MusicPlay:
|
case GlobalAction.MusicPlay:
|
||||||
|
@ -140,6 +140,9 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(content)) return;
|
if (string.IsNullOrEmpty(content)) return;
|
||||||
|
|
||||||
|
// newlines could be contained in API returned user content.
|
||||||
|
content = content.Replace("\n", " ");
|
||||||
|
|
||||||
bottomLinkContainer.AddIcon(icon, text =>
|
bottomLinkContainer.AddIcon(icon, text =>
|
||||||
{
|
{
|
||||||
text.Font = text.Font.With(size: 10);
|
text.Font = text.Font.With(size: 10);
|
||||||
|
@ -196,17 +196,30 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TooltipText => Statistics.Value?.Ranks.Global == null ? "" : $"#{ranks[dayIndex].Value:#,##0}|{ranked_days - ranks[dayIndex].Key + 1}";
|
public object TooltipContent
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Statistics.Value?.Ranks.Global == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var days = ranked_days - ranks[dayIndex].Key + 1;
|
||||||
|
|
||||||
|
return new TooltipDisplayContent
|
||||||
|
{
|
||||||
|
Rank = $"#{ranks[dayIndex].Value:#,##0}",
|
||||||
|
Time = days == 0 ? "now" : $"{days} days ago"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ITooltip GetCustomTooltip() => new RankGraphTooltip();
|
public ITooltip GetCustomTooltip() => new RankGraphTooltip();
|
||||||
|
|
||||||
public class RankGraphTooltip : VisibilityContainer, ITooltip
|
private class RankGraphTooltip : VisibilityContainer, ITooltip
|
||||||
{
|
{
|
||||||
private readonly OsuSpriteText globalRankingText, timeText;
|
private readonly OsuSpriteText globalRankingText, timeText;
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
|
|
||||||
public string TooltipText { get; set; }
|
|
||||||
|
|
||||||
public RankGraphTooltip()
|
public RankGraphTooltip()
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
@ -260,11 +273,14 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
background.Colour = colours.GreySeafoamDark;
|
background.Colour = colours.GreySeafoamDark;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Refresh()
|
public bool SetContent(object content)
|
||||||
{
|
{
|
||||||
var info = TooltipText.Split('|');
|
if (!(content is TooltipDisplayContent info))
|
||||||
globalRankingText.Text = info[0];
|
return false;
|
||||||
timeText.Text = info[1] == "0" ? "now" : $"{info[1]} days ago";
|
|
||||||
|
globalRankingText.Text = info.Rank;
|
||||||
|
timeText.Text = info.Time;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool instantMove = true;
|
private bool instantMove = true;
|
||||||
@ -280,9 +296,24 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
this.MoveTo(pos, 200, Easing.OutQuint);
|
this.MoveTo(pos, 200, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TooltipText
|
||||||
|
{
|
||||||
|
set => throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void PopIn() => this.FadeIn(200, Easing.OutQuint);
|
protected override void PopIn() => this.FadeIn(200, Easing.OutQuint);
|
||||||
|
|
||||||
protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
|
protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TooltipDisplayContent
|
||||||
|
{
|
||||||
|
public string Rank;
|
||||||
|
public string Time;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,8 +46,11 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
|
|||||||
if (!s.OnlineBeatmapSetID.HasValue)
|
if (!s.OnlineBeatmapSetID.HasValue)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var panel = new DirectGridPanel(s.ToBeatmapSet(Rulesets));
|
ItemsContainer.Add(new DirectGridPanel(s.ToBeatmapSet(Rulesets))
|
||||||
ItemsContainer.Add(panel);
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -57,7 +58,12 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
this.drawableRuleset = drawableRuleset;
|
this.drawableRuleset = drawableRuleset;
|
||||||
|
|
||||||
InternalChild = drawableRuleset;
|
InternalChild = drawableRuleset;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
drawableRuleset.FrameStablePlayback = false;
|
||||||
Playfield.DisplayJudgements.Value = false;
|
Playfield.DisplayJudgements.Value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,9 +325,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
|
|
||||||
JudgedHits++;
|
JudgedHits++;
|
||||||
|
|
||||||
if (result.Type != HitResult.None)
|
|
||||||
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
|
|
||||||
|
|
||||||
if (result.Judgement.AffectsCombo)
|
if (result.Judgement.AffectsCombo)
|
||||||
{
|
{
|
||||||
switch (result.Type)
|
switch (result.Type)
|
||||||
@ -352,6 +349,9 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (result.HasResult)
|
||||||
|
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
|
||||||
|
|
||||||
baseScore += result.Judgement.NumericResultFor(result);
|
baseScore += result.Judgement.NumericResultFor(result);
|
||||||
rollingMaxBaseScore += result.Judgement.MaxNumericResult;
|
rollingMaxBaseScore += result.Judgement.MaxNumericResult;
|
||||||
}
|
}
|
||||||
@ -371,9 +371,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
|
|
||||||
JudgedHits--;
|
JudgedHits--;
|
||||||
|
|
||||||
if (result.Type != HitResult.None)
|
|
||||||
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1;
|
|
||||||
|
|
||||||
if (result.Judgement.IsBonus)
|
if (result.Judgement.IsBonus)
|
||||||
{
|
{
|
||||||
if (result.IsHit)
|
if (result.IsHit)
|
||||||
@ -381,6 +378,9 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (result.HasResult)
|
||||||
|
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1;
|
||||||
|
|
||||||
baseScore -= result.Judgement.NumericResultFor(result);
|
baseScore -= result.Judgement.NumericResultFor(result);
|
||||||
rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
|
rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,15 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public override GameplayClock FrameStableClock => frameStabilityContainer.GameplayClock;
|
public override GameplayClock FrameStableClock => frameStabilityContainer.GameplayClock;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to enable frame-stable playback.
|
||||||
|
/// </summary>
|
||||||
|
internal bool FrameStablePlayback
|
||||||
|
{
|
||||||
|
get => frameStabilityContainer.FrameStablePlayback;
|
||||||
|
set => frameStabilityContainer.FrameStablePlayback = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when a <see cref="JudgementResult"/> has been applied by a <see cref="DrawableHitObject"/>.
|
/// Invoked when a <see cref="JudgementResult"/> has been applied by a <see cref="DrawableHitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxCatchUpFrames { get; set; } = 5;
|
public int MaxCatchUpFrames { get; set; } = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to enable frame-stable playback.
|
||||||
|
/// </summary>
|
||||||
|
internal bool FrameStablePlayback = true;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
public GameplayClock GameplayClock { get; }
|
public GameplayClock GameplayClock { get; }
|
||||||
|
|
||||||
@ -113,7 +118,13 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (firstConsumption)
|
if (!FrameStablePlayback)
|
||||||
|
{
|
||||||
|
manualClock.CurrentTime = newProposedTime;
|
||||||
|
requireMoreUpdateLoops = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (firstConsumption)
|
||||||
{
|
{
|
||||||
// On the first update, frame-stability seeking would result in unexpected/unwanted behaviour.
|
// On the first update, frame-stability seeking would result in unexpected/unwanted behaviour.
|
||||||
// Instead we perform an initial seek to the proposed time.
|
// Instead we perform an initial seek to the proposed time.
|
||||||
|
@ -28,11 +28,18 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
private Bindable<bool> menuVoice;
|
private Bindable<bool> menuVoice;
|
||||||
|
|
||||||
|
private LeasedBindable<WorkingBeatmap> beatmap;
|
||||||
|
|
||||||
|
public new Bindable<WorkingBeatmap> Beatmap => beatmap;
|
||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
|
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
|
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
|
||||||
{
|
{
|
||||||
|
// prevent user from changing beatmap while the intro is still runnning.
|
||||||
|
beatmap = base.Beatmap.BeginLease(false);
|
||||||
|
|
||||||
menuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice);
|
menuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice);
|
||||||
seeya = audio.Samples.Get(@"seeya");
|
seeya = audio.Samples.Get(@"seeya");
|
||||||
}
|
}
|
||||||
@ -107,6 +114,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
protected void LoadMenu()
|
protected void LoadMenu()
|
||||||
{
|
{
|
||||||
|
beatmap.Return();
|
||||||
|
|
||||||
DidLoadMenu = true;
|
DidLoadMenu = true;
|
||||||
this.Push(mainMenu);
|
this.Push(mainMenu);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ using osu.Game.Screens.Select.Carousel;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Select
|
namespace osu.Game.Screens.Select
|
||||||
{
|
{
|
||||||
public class BeatmapCarousel : OsuScrollContainer
|
public class BeatmapCarousel : CompositeDrawable
|
||||||
{
|
{
|
||||||
private const float bleed_top = FilterControl.HEIGHT;
|
private const float bleed_top = FilterControl.HEIGHT;
|
||||||
private const float bleed_bottom = Footer.HEIGHT;
|
private const float bleed_bottom = Footer.HEIGHT;
|
||||||
@ -61,6 +61,8 @@ namespace osu.Game.Screens.Select
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool BeatmapSetsLoaded { get; private set; }
|
public bool BeatmapSetsLoaded { get; private set; }
|
||||||
|
|
||||||
|
private readonly OsuScrollContainer scroll;
|
||||||
|
|
||||||
private IEnumerable<CarouselBeatmapSet> beatmapSets => root.Children.OfType<CarouselBeatmapSet>();
|
private IEnumerable<CarouselBeatmapSet> beatmapSets => root.Children.OfType<CarouselBeatmapSet>();
|
||||||
|
|
||||||
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
||||||
@ -110,13 +112,17 @@ namespace osu.Game.Screens.Select
|
|||||||
public BeatmapCarousel()
|
public BeatmapCarousel()
|
||||||
{
|
{
|
||||||
root = new CarouselRoot(this);
|
root = new CarouselRoot(this);
|
||||||
Child = new OsuContextMenuContainer
|
InternalChild = new OsuContextMenuContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.Both,
|
||||||
AutoSizeAxes = Axes.Y,
|
Child = scroll = new CarouselScrollContainer
|
||||||
Child = scrollableContent = new Container<DrawableCarouselItem>
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
Masking = false,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = scrollableContent = new Container<DrawableCarouselItem>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -127,7 +133,7 @@ namespace osu.Game.Screens.Select
|
|||||||
config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
|
config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
|
||||||
config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled);
|
config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled);
|
||||||
|
|
||||||
RightClickScrollingEnabled.ValueChanged += enabled => RightMouseScrollbar = enabled.NewValue;
|
RightClickScrollingEnabled.ValueChanged += enabled => scroll.RightMouseScrollbar = enabled.NewValue;
|
||||||
RightClickScrollingEnabled.TriggerChange();
|
RightClickScrollingEnabled.TriggerChange();
|
||||||
|
|
||||||
loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable());
|
loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable());
|
||||||
@ -351,12 +357,12 @@ namespace osu.Game.Screens.Select
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The position of the lower visible bound with respect to the current scroll position.
|
/// The position of the lower visible bound with respect to the current scroll position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private float visibleBottomBound => Current + DrawHeight + bleed_bottom;
|
private float visibleBottomBound => scroll.Current + DrawHeight + bleed_bottom;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The position of the upper visible bound with respect to the current scroll position.
|
/// The position of the upper visible bound with respect to the current scroll position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private float visibleUpperBound => Current - bleed_top;
|
private float visibleUpperBound => scroll.Current - bleed_top;
|
||||||
|
|
||||||
public void FlushPendingFilterOperations()
|
public void FlushPendingFilterOperations()
|
||||||
{
|
{
|
||||||
@ -628,7 +634,7 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private void updateScrollPosition()
|
private void updateScrollPosition()
|
||||||
{
|
{
|
||||||
if (scrollTarget != null) ScrollTo(scrollTarget.Value);
|
if (scrollTarget != null) scroll.ScrollTo(scrollTarget.Value);
|
||||||
scrollPositionCache.Validate();
|
scrollPositionCache.Validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -688,5 +694,35 @@ namespace osu.Game.Screens.Select
|
|||||||
base.PerformSelection();
|
base.PerformSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class CarouselScrollContainer : OsuScrollContainer
|
||||||
|
{
|
||||||
|
private bool rightMouseScrollBlocked;
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
if (e.Button == MouseButton.Right)
|
||||||
|
{
|
||||||
|
// we need to block right click absolute scrolling when hovering a carousel item so context menus can display.
|
||||||
|
// this can be reconsidered when we have an alternative to right click scrolling.
|
||||||
|
if (GetContainingInputManager().HoveredDrawables.OfType<DrawableCarouselItem>().Any())
|
||||||
|
{
|
||||||
|
rightMouseScrollBlocked = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rightMouseScrollBlocked = false;
|
||||||
|
return base.OnMouseDown(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDragStart(DragStartEvent e)
|
||||||
|
{
|
||||||
|
if (rightMouseScrollBlocked)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return base.OnDragStart(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -402,31 +402,35 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DifficultyColourBar : DifficultyColouredContainer
|
private class DifficultyColourBar : Container
|
||||||
{
|
{
|
||||||
|
private readonly BeatmapInfo beatmap;
|
||||||
|
|
||||||
public DifficultyColourBar(BeatmapInfo beatmap)
|
public DifficultyColourBar(BeatmapInfo beatmap)
|
||||||
: base(beatmap)
|
|
||||||
{
|
{
|
||||||
|
this.beatmap = beatmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
const float full_opacity_ratio = 0.7f;
|
const float full_opacity_ratio = 0.7f;
|
||||||
|
|
||||||
|
var difficultyColour = colours.ForDifficultyRating(beatmap.DifficultyRating);
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = AccentColour,
|
Colour = difficultyColour,
|
||||||
Width = full_opacity_ratio,
|
Width = full_opacity_ratio,
|
||||||
},
|
},
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RelativePositionAxes = Axes.Both,
|
RelativePositionAxes = Axes.Both,
|
||||||
Colour = AccentColour,
|
Colour = difficultyColour,
|
||||||
Alpha = 0.5f,
|
Alpha = 0.5f,
|
||||||
X = full_opacity_ratio,
|
X = full_opacity_ratio,
|
||||||
Width = 1 - full_opacity_ratio,
|
Width = 1 - full_opacity_ratio,
|
||||||
|
@ -82,7 +82,7 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new DifficultyIcon(beatmap)
|
new DifficultyIcon(beatmap, shouldShowTooltip: false)
|
||||||
{
|
{
|
||||||
Scale = new Vector2(1.8f),
|
Scale = new Vector2(1.8f),
|
||||||
},
|
},
|
||||||
|
@ -157,7 +157,6 @@ namespace osu.Game.Screens.Select
|
|||||||
},
|
},
|
||||||
Child = Carousel = new BeatmapCarousel
|
Child = Carousel = new BeatmapCarousel
|
||||||
{
|
{
|
||||||
Masking = false,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Size = new Vector2(1 - wedged_container_size.X, 1),
|
Size = new Vector2(1 - wedged_container_size.X, 1),
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
@ -360,6 +359,7 @@ namespace osu.Game.Screens.Select
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
beatmapNoDebounce = beatmap;
|
beatmapNoDebounce = beatmap;
|
||||||
|
|
||||||
performUpdateSelected();
|
performUpdateSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,10 +587,18 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
Track track = Beatmap.Value.Track;
|
Track track = Beatmap.Value.Track;
|
||||||
|
|
||||||
if ((!track.IsRunning || restart) && music?.IsUserPaused != true)
|
if (!track.IsRunning || restart)
|
||||||
{
|
{
|
||||||
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
|
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
|
||||||
track.Restart();
|
|
||||||
|
if (music != null)
|
||||||
|
{
|
||||||
|
// use the global music controller (when available) to cancel a potential local user paused state.
|
||||||
|
music.SeekTo(track.RestartPoint);
|
||||||
|
music.Play();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
track.Restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Animations;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
@ -42,6 +43,8 @@ namespace osu.Game.Skinning
|
|||||||
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
|
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
|
||||||
: this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, storage), audioManager, "skin.ini")
|
: this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, storage), audioManager, "skin.ini")
|
||||||
{
|
{
|
||||||
|
// defaults should only be applied for non-beatmap skins (which are parsed via this constructor).
|
||||||
|
if (!Configuration.CustomColours.ContainsKey("SliderBall")) Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly bool hasHitCircle;
|
private readonly bool hasHitCircle;
|
||||||
@ -59,7 +62,7 @@ namespace osu.Game.Skinning
|
|||||||
Samples = audioManager.GetSampleStore(storage);
|
Samples = audioManager.GetSampleStore(storage);
|
||||||
Textures = new TextureStore(new TextureLoaderStore(storage));
|
Textures = new TextureStore(new TextureLoaderStore(storage));
|
||||||
|
|
||||||
using (var testStream = storage.GetStream("hitcircle"))
|
using (var testStream = storage.GetStream("hitcircle@2x") ?? storage.GetStream("hitcircle"))
|
||||||
hasHitCircle |= testStream != null;
|
hasHitCircle |= testStream != null;
|
||||||
|
|
||||||
if (hasHitCircle)
|
if (hasHitCircle)
|
||||||
@ -75,8 +78,13 @@ namespace osu.Game.Skinning
|
|||||||
Samples?.Dispose();
|
Samples?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const double default_frame_time = 1000 / 60d;
|
||||||
|
|
||||||
public override Drawable GetDrawableComponent(string componentName)
|
public override Drawable GetDrawableComponent(string componentName)
|
||||||
{
|
{
|
||||||
|
bool animatable = false;
|
||||||
|
bool looping = true;
|
||||||
|
|
||||||
switch (componentName)
|
switch (componentName)
|
||||||
{
|
{
|
||||||
case "Play/osu/cursor":
|
case "Play/osu/cursor":
|
||||||
@ -86,8 +94,20 @@ namespace osu.Game.Skinning
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
case "Play/osu/sliderball":
|
case "Play/osu/sliderball":
|
||||||
if (GetTexture("sliderb") != null)
|
var sliderBallContent = getAnimation("sliderb", true, true, "");
|
||||||
return new LegacySliderBall();
|
|
||||||
|
if (sliderBallContent != null)
|
||||||
|
{
|
||||||
|
var size = sliderBallContent.Size;
|
||||||
|
|
||||||
|
sliderBallContent.RelativeSizeAxes = Axes.Both;
|
||||||
|
sliderBallContent.Size = Vector2.One;
|
||||||
|
|
||||||
|
return new LegacySliderBall(sliderBallContent)
|
||||||
|
{
|
||||||
|
Size = size
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -97,20 +117,32 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
case "Play/osu/sliderfollowcircle":
|
||||||
|
animatable = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case "Play/Miss":
|
case "Play/Miss":
|
||||||
componentName = "hit0";
|
componentName = "hit0";
|
||||||
|
animatable = true;
|
||||||
|
looping = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Play/Meh":
|
case "Play/Meh":
|
||||||
componentName = "hit50";
|
componentName = "hit50";
|
||||||
|
animatable = true;
|
||||||
|
looping = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Play/Good":
|
case "Play/Good":
|
||||||
componentName = "hit100";
|
componentName = "hit100";
|
||||||
|
animatable = true;
|
||||||
|
looping = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Play/Great":
|
case "Play/Great":
|
||||||
componentName = "hit300";
|
componentName = "hit300";
|
||||||
|
animatable = true;
|
||||||
|
looping = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Play/osu/number-text":
|
case "Play/osu/number-text":
|
||||||
@ -124,25 +156,42 @@ namespace osu.Game.Skinning
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporary allowance is given for skins the fact that stable handles non-animatable items such as hitcircles (incorrectly)
|
return getAnimation(componentName, animatable, looping);
|
||||||
// by (incorrectly) displaying the first frame of animation rather than the non-animated version.
|
|
||||||
// users have used this to "hide" certain elements like hit300.
|
|
||||||
var texture = GetTexture($"{componentName}-0") ?? GetTexture(componentName);
|
|
||||||
|
|
||||||
if (texture == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return new Sprite { Texture = texture };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LegacySliderBall : Sprite
|
private Drawable getAnimation(string componentName, bool animatable, bool looping, string animationSeparator = "-")
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
Texture texture;
|
||||||
private void load(ISkinSource skin)
|
|
||||||
|
Texture getFrameTexture(int frame) => GetTexture($"{componentName}{animationSeparator}{frame}");
|
||||||
|
|
||||||
|
TextureAnimation animation = null;
|
||||||
|
|
||||||
|
if (animatable)
|
||||||
{
|
{
|
||||||
Texture = skin.GetTexture("sliderb");
|
for (int i = 0;; i++)
|
||||||
Colour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White;
|
{
|
||||||
|
if ((texture = getFrameTexture(i)) == null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (animation == null)
|
||||||
|
animation = new TextureAnimation
|
||||||
|
{
|
||||||
|
DefaultFrameLength = default_frame_time,
|
||||||
|
Repeat = looping
|
||||||
|
};
|
||||||
|
|
||||||
|
animation.AddFrame(texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (animation != null)
|
||||||
|
return animation;
|
||||||
|
|
||||||
|
if ((texture = GetTexture(componentName)) != null)
|
||||||
|
return new Sprite { Texture = texture };
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Texture GetTexture(string componentName)
|
public override Texture GetTexture(string componentName)
|
||||||
@ -299,6 +348,37 @@ namespace osu.Game.Skinning
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class LegacySliderBall : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly Drawable animationContent;
|
||||||
|
|
||||||
|
public LegacySliderBall(Drawable animationContent)
|
||||||
|
{
|
||||||
|
this.animationContent = animationContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin, DrawableHitObject drawableObject)
|
||||||
|
{
|
||||||
|
animationContent.Colour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White;
|
||||||
|
|
||||||
|
InternalChildren = new[]
|
||||||
|
{
|
||||||
|
new Sprite
|
||||||
|
{
|
||||||
|
Texture = skin.GetTexture("sliderb-nd"),
|
||||||
|
Colour = new Color4(5, 5, 5, 255),
|
||||||
|
},
|
||||||
|
animationContent,
|
||||||
|
new Sprite
|
||||||
|
{
|
||||||
|
Texture = skin.GetTexture("sliderb-spec"),
|
||||||
|
Blending = BlendingMode.Additive,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class LegacyMainCirclePiece : CompositeDrawable
|
public class LegacyMainCirclePiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
public LegacyMainCirclePiece()
|
public LegacyMainCirclePiece()
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2019.813.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2019.816.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.23.0" />
|
<PackageReference Include="SharpCompress" Version="0.23.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
@ -118,8 +118,8 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2019.813.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2019.816.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.813.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.816.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.22.0" />
|
<PackageReference Include="SharpCompress" Version="0.22.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
@ -31,6 +31,10 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>UIStatusBarHidden</key>
|
<key>UIStatusBarHidden</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>We don't really use the camera.</string>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>We don't really use the microphone.</string>
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|