diff --git a/appveyor.yml b/appveyor.yml
index 4dcaa7b45e..be1727e7d7 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -2,7 +2,5 @@ clone_depth: 1
version: '{branch}-{build}'
image: Previous Visual Studio 2017
test: off
-install:
- - cmd: git submodule update --init --recursive --depth=5
build_script:
- cmd: PowerShell -Version 2.0 .\build.ps1
diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml
new file mode 100644
index 0000000000..d36298175b
--- /dev/null
+++ b/appveyor_deploy.yml
@@ -0,0 +1,10 @@
+clone_depth: 1
+version: '{build}'
+image: Previous Visual Studio 2017
+test: off
+skip_non_tags: true
+build_script:
+ - cmd: PowerShell -Version 2.0 .\build.ps1
+deploy:
+ - provider: Environment
+ name: nuget
diff --git a/osu.Android.props b/osu.Android.props
index 90d1854c39..adc340a734 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -61,6 +61,6 @@
-
+
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
index e96c7d8f92..720ef1db42 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
@@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osuTK.Graphics;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
@@ -99,8 +100,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public Texture GetTexture(string componentName) =>
throw new NotImplementedException();
- public TValue GetValue(Func query) where TConfiguration : SkinConfiguration =>
- throw new NotImplementedException();
+ public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException();
}
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index 19a1b59752..a25d9cb67e 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -6,6 +6,7 @@ using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Objects
{
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs b/osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs
similarity index 87%
rename from osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs
rename to osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs
index 837662f5fe..ff793a372e 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs
@@ -1,10 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
-namespace osu.Game.Rulesets.Catch.Objects
+namespace osu.Game.Rulesets.Catch.Scoring
{
public class CatchHitWindows : HitWindows
{
diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index 99b22b2d56..18785d65ea 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -4,7 +4,6 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
index d945abdb04..37cba1fd3c 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
@@ -11,9 +11,9 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
using osu.Game.Rulesets.Mania.Difficulty.Skills;
using osu.Game.Rulesets.Mania.Mods;
-using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Difficulty
{
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
StarRating = difficultyValue(skills) * star_scaling_factor,
Mods = mods,
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
- GreatHitWindow = (int)(hitWindows.Great / 2) / clockRate,
+ GreatHitWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate,
Skills = skills
};
}
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
index d28d04b3c1..0c82cf7bbc 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
@@ -5,8 +5,8 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects
{
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
index 6bb21633b6..d0125f8793 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
@@ -3,7 +3,7 @@
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
-using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects
{
diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
index 70720a926b..995e1516cb 100644
--- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
@@ -3,7 +3,9 @@
using osu.Framework.Bindables;
using osu.Game.Rulesets.Mania.Objects.Types;
+using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects
{
diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs
deleted file mode 100644
index 5f2ceab48b..0000000000
--- a/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.Collections.Generic;
-using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Scoring;
-
-namespace osu.Game.Rulesets.Mania.Objects
-{
- public class ManiaHitWindows : HitWindows
- {
- private static readonly IReadOnlyDictionary base_ranges = new Dictionary
- {
- { HitResult.Perfect, (44.8, 38.8, 27.8) },
- { HitResult.Great, (128, 98, 68) },
- { HitResult.Good, (194, 164, 134) },
- { HitResult.Ok, (254, 224, 194) },
- { HitResult.Meh, (302, 272, 242) },
- { HitResult.Miss, (376, 346, 316) },
- };
-
- public override bool IsHitResultAllowed(HitResult result) => true;
-
- public override void SetDifficulty(double difficulty)
- {
- Perfect = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Perfect]);
- Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]);
- Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]);
- Ok = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Ok]);
- Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]);
- Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs
new file mode 100644
index 0000000000..549f0f9214
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs
@@ -0,0 +1,11 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Mania.Scoring
+{
+ public class ManiaHitWindows : HitWindows
+ {
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 5caf08fb1e..49894a644c 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -4,7 +4,6 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Objects;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs
index 585fdb9cb4..863d0eda09 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Debug.Assert(drawableHitObject.HitObject.HitWindows != null);
- double delay = drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current;
+ double delay = drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.WindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current;
Scheduler.AddDelayed(() => drawableHitObject.TriggerJudgement(), delay);
return drawableHitObject;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs
index fe73e7c861..02c65db6ad 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs
@@ -7,6 +7,7 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
@@ -135,6 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public SampleChannel GetSample(ISampleInfo sampleInfo) => null;
public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => default;
+ public IBindable GetConfig(TLookup lookup) => null;
public event Action SourceChanged;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 61e9f60cdd..b0d261a1cc 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -9,11 +9,12 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Difficulty.Skills;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Scoring;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Difficulty
{
@@ -39,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
- double hitWindowGreat = (int)(hitWindows.Great / 2) / clockRate;
+ double hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate;
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
int maxCombo = beatmap.HitObjects.Count;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 3decc4e51f..985dcbca86 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -10,8 +10,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
-using osuTK;
using osu.Game.Rulesets.Scoring;
+using osuTK;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
@@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (result == HitResult.None)
{
- Shake(Math.Abs(timeOffset) - HitObject.HitWindows.HalfWindowFor(HitResult.Miss));
+ Shake(Math.Abs(timeOffset) - HitObject.HitWindows.WindowFor(HitResult.Miss));
return;
}
@@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
hitArea.HitAction = null;
// override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early.
- LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss);
+ LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.WindowFor(HitResult.Miss);
break;
case ArmedState.Miss:
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
index 50187781f6..00a943a67f 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
@@ -9,8 +9,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Drawables;
-using osuTK;
using osu.Game.Rulesets.Scoring;
+using osuTK;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 1749ea1f60..00c953c393 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -12,6 +12,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Configuration;
+using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
using osu.Game.Skinning;
@@ -166,12 +167,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.SkinChanged(skin, allowFallback);
- Body.BorderSize = skin.GetValue(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE;
- sliderPathRadius = skin.GetValue(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS;
+ Body.BorderSize = skin.GetConfig(OsuSkinConfiguration.SliderBorderSize)?.Value ?? SliderBody.DEFAULT_BORDER_SIZE;
+ sliderPathRadius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
updatePathRadius();
- Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value;
- Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White;
+ Body.AccentColour = skin.GetConfig(OsuSkinColour.SliderTrackOverride)?.Value ?? AccentColour.Value;
+ Body.BorderColour = skin.GetConfig(OsuSkinColour.SliderBorder)?.Value ?? Color4.White;
}
private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index c5fa5f0af5..ba931976a8 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -8,9 +8,9 @@ using osu.Game.Rulesets.Objects.Drawables;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index a0bd301fdb..49aaa2aaea 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -13,8 +13,8 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
-using osu.Game.Screens.Ranking;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Screens.Ranking;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index 7c871c6ccd..ef7b077480 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -11,6 +11,7 @@ using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Skinning;
using osuTK.Graphics;
using osu.Game.Skinning;
using osuTK;
@@ -218,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
RelativeSizeAxes = Axes.Both;
- float radius = skin.GetValue(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS;
+ float radius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
InternalChild = new CircularContainer
{
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index b52bfcd181..2cf877b000 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -7,6 +7,8 @@ using osu.Game.Rulesets.Objects;
using osuTK;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Osu.Scoring;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs
deleted file mode 100644
index add8fd53c7..0000000000
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.Collections.Generic;
-using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Scoring;
-
-namespace osu.Game.Rulesets.Osu.Objects
-{
- public class OsuHitWindows : HitWindows
- {
- private static readonly IReadOnlyDictionary base_ranges = new Dictionary
- {
- { HitResult.Great, (160, 100, 40) },
- { HitResult.Good, (280, 200, 120) },
- { HitResult.Meh, (400, 300, 200) },
- { HitResult.Miss, (400, 400, 400) },
- };
-
- public override void SetDifficulty(double difficulty)
- {
- Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]);
- Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]);
- Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]);
- Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
index 5bd480c0ff..a794e57c9e 100644
--- a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
@@ -5,8 +5,8 @@ using System;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 93231844bb..2805494021 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -13,6 +13,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
index c53a88337e..7e540a577b 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
@@ -5,6 +5,7 @@ using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
index 60e9084ed3..af7cf5b144 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
@@ -4,8 +4,8 @@
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
index 69c779a182..2e7b763966 100644
--- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
@@ -6,8 +6,8 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index 27899ab56e..ceb9ed9343 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu
public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this);
- public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkin(source);
+ public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkinTransformer(source);
public override int? LegacyID => 0;
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
index e5fa571d4d..24320b6579 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
@@ -10,9 +10,9 @@ using System.Diagnostics;
using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Replays;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Beatmaps;
+using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Replays
@@ -118,29 +118,29 @@ namespace osu.Game.Rulesets.Osu.Replays
Debug.Assert(hitWindows != null);
// Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
- if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Miss) > endTime + hitWindows.HalfWindowFor(HitResult.Meh) + 50)
+ if (h.StartTime - hitWindows.WindowFor(HitResult.Miss) > endTime + hitWindows.WindowFor(HitResult.Meh) + 50)
{
if (!(prev is Spinner) && h.StartTime - endTime < 1000)
- AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
+ AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
if (!(h is Spinner))
- AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
+ AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
}
- else if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Meh) > endTime + hitWindows.HalfWindowFor(HitResult.Meh) + 50)
+ else if (h.StartTime - hitWindows.WindowFor(HitResult.Meh) > endTime + hitWindows.WindowFor(HitResult.Meh) + 50)
{
if (!(prev is Spinner) && h.StartTime - endTime < 1000)
- AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
+ AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
if (!(h is Spinner))
- AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
+ AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
}
- else if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Good) > endTime + hitWindows.HalfWindowFor(HitResult.Good) + 50)
+ else if (h.StartTime - hitWindows.WindowFor(HitResult.Good) > endTime + hitWindows.WindowFor(HitResult.Good) + 50)
{
if (!(prev is Spinner) && h.StartTime - endTime < 1000)
- AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
+ AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
if (!(h is Spinner))
- AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
+ AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
}
}
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs
new file mode 100644
index 0000000000..a6491bb3f3
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs
@@ -0,0 +1,34 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Osu.Scoring
+{
+ public class OsuHitWindows : HitWindows
+ {
+ private static readonly DifficultyRange[] osu_ranges =
+ {
+ new DifficultyRange(HitResult.Great, 80, 50, 20),
+ new DifficultyRange(HitResult.Good, 140, 100, 60),
+ new DifficultyRange(HitResult.Meh, 200, 150, 100),
+ new DifficultyRange(HitResult.Miss, 200, 200, 200),
+ };
+
+ public override bool IsHitResultAllowed(HitResult result)
+ {
+ switch (result)
+ {
+ case HitResult.Great:
+ case HitResult.Good:
+ case HitResult.Meh:
+ case HitResult.Miss:
+ return true;
+ }
+
+ return false;
+ }
+
+ protected override DifficultyRange[] GetRanges() => osu_ranges;
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index 66ef020d09..affe18a30d 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
-using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
@@ -22,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
private float hpDrainRate;
- private readonly Dictionary comboResultCounts = new Dictionary();
-
protected override void ApplyBeatmap(Beatmap beatmap)
{
base.ApplyBeatmap(beatmap);
@@ -31,22 +27,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
}
- protected override void Reset(bool storeResults)
- {
- base.Reset(storeResults);
- comboResultCounts.Clear();
- }
-
- protected override void ApplyResult(JudgementResult result)
- {
- base.ApplyResult(result);
-
- var osuResult = (OsuJudgementResult)result;
-
- if (result.Type != HitResult.None)
- comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1;
- }
-
protected override double HealthAdjustmentFactorFor(JudgementResult result)
{
switch (result.Type)
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
index ec838c596d..81c02199d0 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
[BackgroundDependencyLoader]
private void load(ISkinSource skin, DrawableHitObject drawableObject)
{
- animationContent.Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White;
+ animationContent.Colour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White;
InternalChildren = new[]
{
diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
similarity index 70%
rename from osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs
rename to osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
index e3e302b81c..5957b81d7e 100644
--- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
@@ -3,21 +3,19 @@
using System;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
using osu.Game.Skinning;
using osuTK;
-using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Skinning
{
- public class OsuLegacySkin : ISkin
+ public class OsuLegacySkinTransformer : ISkin
{
private readonly ISkin source;
- private Lazy configuration;
-
private Lazy hasHitCircle;
///
@@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
///
private const float legacy_circle_radius = 64 - 5;
- public OsuLegacySkin(ISkinSource source)
+ public OsuLegacySkinTransformer(ISkinSource source)
{
this.source = source;
@@ -37,21 +35,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
private void sourceChanged()
{
- // these need to be lazy in order to ensure they aren't called before the dependencies have been loaded into our source.
- configuration = new Lazy(() =>
- {
- var config = new SkinConfiguration();
- if (hasHitCircle.Value)
- config.SliderPathRadius = legacy_circle_radius;
-
- // defaults should only be applied for non-beatmap skins (which are parsed via this constructor).
- config.CustomColours["SliderBall"] =
- source.GetValue(s => s.CustomColours.TryGetValue("SliderBall", out var val) ? val : (Color4?)null)
- ?? new Color4(2, 170, 255, 255);
-
- return config;
- });
-
hasHitCircle = new Lazy(() => source.GetTexture("hitcircle") != null);
}
@@ -96,8 +79,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
return null;
case OsuSkinComponents.HitCircleText:
- string font = GetValue(config => config.HitCircleFont);
- var overlap = GetValue(config => config.HitCircleOverlap);
+ var font = GetConfig(OsuSkinConfiguration.HitCircleFont)?.Value ?? "default";
+ var overlap = GetConfig(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0;
return !hasFont(font)
? null
@@ -116,13 +99,27 @@ namespace osu.Game.Rulesets.Osu.Skinning
public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample);
- public TValue GetValue(Func query) where TConfiguration : SkinConfiguration
+ public IBindable GetConfig(TLookup lookup)
{
- TValue val;
- if (configuration.Value is TConfiguration conf && (val = query.Invoke(conf)) != null)
- return val;
+ switch (lookup)
+ {
+ case OsuSkinColour colour:
+ return source.GetConfig(new SkinCustomColourLookup(colour));
- return source.GetValue(query);
+ case OsuSkinConfiguration osuLookup:
+ switch (osuLookup)
+ {
+ case OsuSkinConfiguration.SliderPathRadius:
+ if (hasHitCircle.Value)
+ return SkinUtils.As(new BindableFloat(legacy_circle_radius));
+
+ break;
+ }
+
+ break;
+ }
+
+ return source.GetConfig(lookup);
}
private bool hasFont(string fontName) => source.GetTexture($"{fontName}-0") != null;
diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs
new file mode 100644
index 0000000000..4e6d3ef0e4
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs
@@ -0,0 +1,12 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Rulesets.Osu.Skinning
+{
+ public enum OsuSkinColour
+ {
+ SliderTrackOverride,
+ SliderBorder,
+ SliderBall
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs
new file mode 100644
index 0000000000..a6b87150ae
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs
@@ -0,0 +1,14 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Rulesets.Osu.Skinning
+{
+ public enum OsuSkinConfiguration
+ {
+ HitCircleFont,
+ HitCircleOverlap,
+ SliderBorderSize,
+ SliderPathRadius,
+ CursorExpand,
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
index 869c27dcac..41a02deaca 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
+using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
@@ -22,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private bool cursorExpand;
- private Bindable cursorScale;
+ private Bindable cursorScale;
private Bindable autoCursorScale;
private readonly IBindable beatmap = new Bindable();
@@ -38,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
- cursorExpand = skin.GetValue(s => s.CursorExpand ?? true);
+ cursorExpand = skin.GetConfig(OsuSkinConfiguration.CursorExpand)?.Value ?? true;
}
[BackgroundDependencyLoader]
@@ -59,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
this.beatmap.BindTo(beatmap);
this.beatmap.ValueChanged += _ => calculateScale();
- cursorScale = config.GetBindable(OsuSetting.GameplayCursorSize);
+ cursorScale = config.GetBindable(OsuSetting.GameplayCursorSize);
cursorScale.ValueChanged += _ => calculateScale();
autoCursorScale = config.GetBindable(OsuSetting.AutoCursorSize);
@@ -70,12 +71,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private void calculateScale()
{
- float scale = (float)cursorScale.Value;
+ float scale = cursorScale.Value;
if (autoCursorScale.Value && beatmap.Value != null)
{
// if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
- scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
+ scale *= 1f - 0.7f * (1f + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY;
}
scaleTarget.Scale = new Vector2(scale);
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
index 6fd16c213b..3aa461e779 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
@@ -14,13 +14,13 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Tests.Visual;
using osuTK;
-using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Tests
{
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
index fc93bccb94..32d49ea39c 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs
@@ -8,11 +8,12 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
using osu.Game.Rulesets.Taiko.Difficulty.Skills;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects;
+using osu.Game.Rulesets.Taiko.Scoring;
namespace osu.Game.Rulesets.Taiko.Difficulty
{
@@ -38,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
StarRating = skills.Single().DifficultyValue() * star_scaling_factor,
Mods = mods,
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
- GreatHitWindow = (int)(hitWindows.Great / 2) / clockRate,
+ GreatHitWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate,
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
Skills = skills
};
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index 0942b37f58..676ecd5a0b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
validActionPressed = false;
UnproxyContent();
- this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire();
+ this.Delay(HitObject.HitWindows.WindowFor(HitResult.Miss)).Expire();
break;
case ArmedState.Miss:
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
index 3ed52f21f0..4e02c76a8b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
@@ -6,7 +6,7 @@ using System;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
index 39e2b45e24..c466ca7c8a 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
diff --git a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs
index 830e640242..d660149528 100644
--- a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
index e7812841bf..f96c033dce 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
@@ -4,7 +4,7 @@
using System;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
diff --git a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs
index 049fa7de5f..68212e8f12 100644
--- a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
index 3592d73004..6f4fbd0651 100644
--- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
@@ -4,7 +4,9 @@
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
+using osu.Game.Rulesets.Taiko.Scoring;
namespace osu.Game.Rulesets.Taiko.Objects
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs
deleted file mode 100644
index f232919cbf..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.Collections.Generic;
-using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Scoring;
-
-namespace osu.Game.Rulesets.Taiko.Objects
-{
- public class TaikoHitWindows : HitWindows
- {
- private static readonly IReadOnlyDictionary base_ranges = new Dictionary
- {
- { HitResult.Great, (100, 70, 40) },
- { HitResult.Good, (240, 160, 100) },
- { HitResult.Miss, (270, 190, 140) },
- };
-
- public override bool IsHitResultAllowed(HitResult result)
- {
- switch (result)
- {
- case HitResult.Great:
- case HitResult.Good:
- case HitResult.Miss:
- return true;
-
- default:
- return false;
- }
- }
-
- public override void SetDifficulty(double difficulty)
- {
- Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]);
- Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]);
- Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs
new file mode 100644
index 0000000000..9d273392ff
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs
@@ -0,0 +1,32 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Taiko.Scoring
+{
+ public class TaikoHitWindows : HitWindows
+ {
+ private static readonly DifficultyRange[] taiko_ranges =
+ {
+ new DifficultyRange(HitResult.Great, 50, 35, 20),
+ new DifficultyRange(HitResult.Good, 120, 80, 50),
+ new DifficultyRange(HitResult.Miss, 135, 95, 70),
+ };
+
+ public override bool IsHitResultAllowed(HitResult result)
+ {
+ switch (result)
+ {
+ case HitResult.Great:
+ case HitResult.Good:
+ case HitResult.Miss:
+ return true;
+ }
+
+ return false;
+ }
+
+ protected override DifficultyRange[] GetRanges() => taiko_ranges;
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index 68ddf2db19..75a27ff639 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -3,7 +3,6 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI;
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index a725c58462..4859abbb8e 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -9,8 +9,8 @@ using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO.Serialization;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Tests.Resources;
using osuTK;
diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs
index 0d6ed67767..9b4a90e9a9 100644
--- a/osu.Game.Tests/Chat/MessageFormatterTests.cs
+++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs
@@ -119,6 +119,53 @@ namespace osu.Game.Tests.Chat
Assert.AreEqual(11, result.Links[0].Length);
}
+ [Test]
+ public void TestOldFormatLinkWithBalancedBrackets()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a (tricky (one))[https://osu.ppy.sh]!" });
+
+ Assert.AreEqual("This is a tricky (one)!", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(10, result.Links[0].Index);
+ Assert.AreEqual(12, result.Links[0].Length);
+ }
+
+ [Test]
+ public void TestOldFormatLinkWithEscapedBrackets()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This is (another loose bracket \\))[https://osu.ppy.sh]." });
+
+ Assert.AreEqual("This is another loose bracket ).", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(8, result.Links[0].Index);
+ Assert.AreEqual(23, result.Links[0].Length);
+ }
+
+ [Test]
+ public void TestOldFormatWithBackslashes()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This link (should end with a backslash \\)[https://osu.ppy.sh]." });
+ Assert.AreEqual("This link should end with a backslash \\.", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(10, result.Links[0].Index);
+ Assert.AreEqual(29, result.Links[0].Length);
+ }
+
+ [Test]
+ public void TestOldFormatLinkWithEscapedAndBalancedBrackets()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a (\\)super\\(\\( tricky (one))[https://osu.ppy.sh]!" });
+
+ Assert.AreEqual("This is a )super(( tricky (one)!", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(10, result.Links[0].Index);
+ Assert.AreEqual(21, result.Links[0].Length);
+ }
+
[Test]
public void TestNewFormatLink()
{
@@ -131,6 +178,42 @@ namespace osu.Game.Tests.Chat
Assert.AreEqual(11, result.Links[0].Length);
}
+ [Test]
+ public void TestNewFormatLinkWithEscapedBrackets()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh nasty link with escaped brackets: \\] and \\[]" });
+
+ Assert.AreEqual("This is a nasty link with escaped brackets: ] and [", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(10, result.Links[0].Index);
+ Assert.AreEqual(41, result.Links[0].Length);
+ }
+
+ [Test]
+ public void TestNewFormatLinkWithBackslashesInside()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh link \\ with \\ backslashes \\]" });
+
+ Assert.AreEqual("This is a link \\ with \\ backslashes \\", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(10, result.Links[0].Index);
+ Assert.AreEqual(27, result.Links[0].Length);
+ }
+
+ [Test]
+ public void TestNewFormatLinkWithEscapedAndBalancedBrackets()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh [link [with \\] too many brackets \\[ ]]]" });
+
+ Assert.AreEqual("This is a [link [with ] too many brackets [ ]]", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(10, result.Links[0].Index);
+ Assert.AreEqual(36, result.Links[0].Length);
+ }
+
[Test]
public void TestMarkdownFormatLink()
{
@@ -143,6 +226,53 @@ namespace osu.Game.Tests.Chat
Assert.AreEqual(11, result.Links[0].Length);
}
+ [Test]
+ public void TestMarkdownFormatLinkWithBalancedBrackets()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [tricky [one]](https://osu.ppy.sh)!" });
+
+ Assert.AreEqual("This is a tricky [one]!", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(10, result.Links[0].Index);
+ Assert.AreEqual(12, result.Links[0].Length);
+ }
+
+ [Test]
+ public void TestMarkdownFormatLinkWithEscapedBrackets()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This is [another loose bracket \\]](https://osu.ppy.sh)." });
+
+ Assert.AreEqual("This is another loose bracket ].", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(8, result.Links[0].Index);
+ Assert.AreEqual(23, result.Links[0].Length);
+ }
+
+ [Test]
+ public void TestMarkdownFormatWithBackslashes()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This link [should end with a backslash \\](https://osu.ppy.sh)." });
+ Assert.AreEqual("This link should end with a backslash \\.", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(10, result.Links[0].Index);
+ Assert.AreEqual(29, result.Links[0].Length);
+ }
+
+ [Test]
+ public void TestMarkdownFormatLinkWithEscapedAndBalancedBrackets()
+ {
+ Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [\\]super\\[\\[ tricky [one]](https://osu.ppy.sh)!" });
+
+ Assert.AreEqual("This is a ]super[[ tricky [one]!", result.DisplayContent);
+ Assert.AreEqual(1, result.Links.Count);
+ Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
+ Assert.AreEqual(10, result.Links[0].Index);
+ Assert.AreEqual(21, result.Links[0].Length);
+ }
+
[Test]
public void TestChannelLink()
{
diff --git a/osu.Game.Tests/Resources/skin.ini b/osu.Game.Tests/Resources/skin.ini
index 0e5737b4ea..7f7f0b32a6 100644
--- a/osu.Game.Tests/Resources/skin.ini
+++ b/osu.Game.Tests/Resources/skin.ini
@@ -1,5 +1,6 @@
[General]
Name: test skin
+TestLookup: TestValue
[Colours]
Combo1 : 142,199,255
diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
index 24ef9e4535..8bd846518b 100644
--- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
+++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
@@ -41,5 +41,20 @@ namespace osu.Game.Tests.Skins
Assert.AreEqual(expectedColors[i], comboColors[i]);
}
}
+
+ [Test]
+ public void TestDecodeGeneral()
+ {
+ var decoder = new LegacySkinDecoder();
+
+ using (var resStream = TestResources.OpenResource("skin.ini"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var config = decoder.Decode(stream);
+
+ Assert.AreEqual("test skin", config.SkinInfo.Name);
+ Assert.AreEqual("TestValue", config.ConfigDictionary["TestLookup"]);
+ }
+ }
}
}
diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs
new file mode 100644
index 0000000000..bbcc4140a9
--- /dev/null
+++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs
@@ -0,0 +1,156 @@
+// Copyright (c) ppy Pty Ltd . 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.Audio.Sample;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Audio;
+using osu.Game.Skinning;
+using osu.Game.Tests.Visual;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Skins
+{
+ [TestFixture]
+ public class TestSceneSkinConfigurationLookup : OsuTestScene
+ {
+ private LegacySkin source1;
+ private LegacySkin source2;
+ private SkinRequester requester;
+
+ [SetUp]
+ public void SetUp() => Schedule(() =>
+ {
+ Add(new SkinProvidingContainer(source1 = new SkinSource())
+ .WithChild(new SkinProvidingContainer(source2 = new SkinSource())
+ .WithChild(requester = new SkinRequester())));
+ });
+
+ [Test]
+ public void TestBasicLookup()
+ {
+ AddStep("Add config values", () =>
+ {
+ source1.Configuration.ConfigDictionary["Lookup"] = "source1";
+ source2.Configuration.ConfigDictionary["Lookup"] = "source2";
+ });
+
+ AddAssert("Check lookup finds source2", () => requester.GetConfig("Lookup")?.Value == "source2");
+ }
+
+ [Test]
+ public void TestFloatLookup()
+ {
+ AddStep("Add config values", () => source1.Configuration.ConfigDictionary["FloatTest"] = "1.1");
+ AddAssert("Check float parse lookup", () => requester.GetConfig("FloatTest")?.Value == 1.1f);
+ }
+
+ [Test]
+ public void TestBoolLookup()
+ {
+ AddStep("Add config values", () => source1.Configuration.ConfigDictionary["BoolTest"] = "1");
+ AddAssert("Check bool parse lookup", () => requester.GetConfig("BoolTest")?.Value == true);
+ }
+
+ [Test]
+ public void TestEnumLookup()
+ {
+ AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Test"] = "Test2");
+ AddAssert("Check enum parse lookup", () => requester.GetConfig(LookupType.Test)?.Value == ValueType.Test2);
+ }
+
+ [Test]
+ public void TestLookupFailure()
+ {
+ AddAssert("Check lookup failure", () => requester.GetConfig("Lookup") == null);
+ }
+
+ [Test]
+ public void TestLookupNull()
+ {
+ AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Lookup"] = null);
+
+ AddAssert("Check lookup null", () =>
+ {
+ var bindable = requester.GetConfig("Lookup");
+ return bindable != null && bindable.Value == null;
+ });
+ }
+
+ [Test]
+ public void TestColourLookup()
+ {
+ AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red);
+ AddAssert("Check colour lookup", () => requester.GetConfig(new SkinCustomColourLookup("Lookup"))?.Value == Color4.Red);
+ }
+
+ [Test]
+ public void TestGlobalLookup()
+ {
+ AddAssert("Check combo colours", () => requester.GetConfig>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0);
+ }
+
+ [Test]
+ public void TestWrongColourType()
+ {
+ AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red);
+
+ AddAssert("perform incorrect lookup", () =>
+ {
+ try
+ {
+ requester.GetConfig(new SkinCustomColourLookup("Lookup"));
+ return false;
+ }
+ catch
+ {
+ return true;
+ }
+ });
+ }
+
+ public enum LookupType
+ {
+ Test
+ }
+
+ public enum ValueType
+ {
+ Test1,
+ Test2,
+ Test3
+ }
+
+ public class SkinSource : LegacySkin
+ {
+ public SkinSource()
+ : base(new SkinInfo(), null, null, string.Empty)
+ {
+ }
+ }
+
+ public class SkinRequester : Drawable, ISkin
+ {
+ private ISkinSource skin;
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin)
+ {
+ this.skin = skin;
+ }
+
+ public Drawable GetDrawableComponent(ISkinComponent component) => skin.GetDrawableComponent(component);
+
+ public Texture GetTexture(string componentName) => skin.GetTexture(componentName);
+
+ public SampleChannel GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
+
+ public IBindable GetConfig(TLookup lookup) => skin.GetConfig(lookup);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs
index e9c15dab9b..a934d22b5d 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs
@@ -3,18 +3,18 @@
using NUnit.Framework;
using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Taiko.Objects;
-using osu.Game.Rulesets.Mania.Objects;
-using osu.Game.Rulesets.Catch.Objects;
using System;
using System.Collections.Generic;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Scoring;
using osu.Framework.MathUtils;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Rulesets.Catch.Scoring;
+using osu.Game.Rulesets.Mania.Scoring;
+using osu.Game.Rulesets.Osu.Scoring;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.Taiko.Scoring;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
namespace osu.Game.Tests.Visual.Gameplay
@@ -36,8 +36,8 @@ namespace osu.Game.Tests.Visual.Gameplay
AddRepeatStep("New random judgement", () => newJudgement(), 40);
- AddRepeatStep("New max negative", () => newJudgement(-hitWindows.HalfWindowFor(HitResult.Meh)), 20);
- AddRepeatStep("New max positive", () => newJudgement(hitWindows.HalfWindowFor(HitResult.Meh)), 20);
+ AddRepeatStep("New max negative", () => newJudgement(-hitWindows.WindowFor(HitResult.Meh)), 20);
+ AddRepeatStep("New max positive", () => newJudgement(hitWindows.WindowFor(HitResult.Meh)), 20);
AddStep("New fixed judgement (50ms)", () => newJudgement(50));
}
@@ -85,9 +85,9 @@ namespace osu.Game.Tests.Visual.Gameplay
AutoSizeAxes = Axes.Both,
Children = new[]
{
- new SpriteText { Text = $@"Great: {hitWindows?.Great}" },
- new SpriteText { Text = $@"Good: {hitWindows?.Good}" },
- new SpriteText { Text = $@"Meh: {hitWindows?.Meh}" },
+ new SpriteText { Text = $@"Great: {hitWindows?.WindowFor(HitResult.Great)}" },
+ new SpriteText { Text = $@"Good: {hitWindows?.WindowFor(HitResult.Good)}" },
+ new SpriteText { Text = $@"Meh: {hitWindows?.WindowFor(HitResult.Meh)}" },
}
});
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
new file mode 100644
index 0000000000..d57ec44f39
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
@@ -0,0 +1,52 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.UI;
+using osu.Game.Scoring;
+using osu.Game.Screens.Play;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ public class TestSceneFailJudgement : AllPlayersTestScene
+ {
+ protected override Player CreatePlayer(Ruleset ruleset)
+ {
+ Mods.Value = Array.Empty();
+
+ var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty());
+ return new FailPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap));
+ }
+
+ protected override void AddCheckSteps()
+ {
+ AddUntilStep("wait for fail", () => Player.HasFailed);
+ AddUntilStep("wait for multiple judged objects", () => ((FailPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.Count(h => h.AllJudged) > 1);
+ AddAssert("total judgements == 1", () => ((FailPlayer)Player).ScoreProcessor.JudgedHits == 1);
+ }
+
+ private class FailPlayer : ReplayPlayer
+ {
+ public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
+
+ public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
+
+ protected override bool PauseOnFocusLost => false;
+
+ public FailPlayer(Score score)
+ : base(score, false, false)
+ {
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ ScoreProcessor.FailConditions += (_, __) => true;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs
index cc275009ba..c1635ffc83 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs
@@ -17,7 +17,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
- [System.ComponentModel.Description("player pause/fail screens")]
+ [Description("player pause/fail screens")]
public class TestSceneGameplayMenuOverlay : ManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) };
@@ -152,7 +152,8 @@ namespace osu.Game.Tests.Visual.Gameplay
}
///
- /// Tests that entering menu with cursor initially on button selects it.
+ /// Tests that entering menu with cursor initially on button doesn't selects it immediately.
+ /// This is to allow for stable keyboard navigation.
///
[Test]
public void TestInitialButtonHover()
@@ -164,6 +165,10 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("Hide overlay", () => pauseOverlay.Hide());
showOverlay();
+ AddAssert("First button not selected", () => !getButton(0).Selected.Value);
+
+ AddStep("Move slightly", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(1)));
+
AddAssert("First button selected", () => getButton(0).Selected.Value);
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
index ee5552c6e0..b3d4820737 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
@@ -5,7 +5,9 @@ using System;
using System.Globalization;
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -133,11 +135,54 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
}
+ [Test]
+ public void TestSwitchOff()
+ {
+ SkinConsumer consumer = null;
+ SwitchableSkinProvidingContainer target = null;
+
+ AddStep("setup layout", () =>
+ {
+ Child = new SkinSourceContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = target = new SwitchableSkinProvidingContainer(new SecondarySource())
+ {
+ RelativeSizeAxes = Axes.Both,
+ }
+ };
+ });
+
+ AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)));
+ AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
+ AddStep("disable", () => target.Disable());
+ AddAssert("consumer using base source", () => consumer.Drawable is BaseSourceBox);
+ }
+
+ private class SwitchableSkinProvidingContainer : SkinProvidingContainer
+ {
+ private bool allow = true;
+
+ protected override bool AllowDrawableLookup(ISkinComponent component) => allow;
+
+ public void Disable()
+ {
+ allow = false;
+ TriggerSourceChanged();
+ }
+
+ public SwitchableSkinProvidingContainer(ISkin skin)
+ : base(skin)
+ {
+ }
+ }
+
private class ExposedSkinnableDrawable : SkinnableDrawable
{
public new Drawable Drawable => base.Drawable;
- public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
+ public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null,
+ ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode)
{
}
@@ -256,7 +301,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
- public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
+ public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException();
}
private class SecondarySource : ISkin
@@ -267,10 +312,11 @@ namespace osu.Game.Tests.Visual.Gameplay
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
- public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
+ public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException();
}
- private class SkinSourceContainer : Container, ISkin
+ [Cached(typeof(ISkinSource))]
+ private class SkinSourceContainer : Container, ISkinSource
{
public Drawable GetDrawableComponent(ISkinComponent componentName) => new BaseSourceBox();
@@ -278,7 +324,9 @@ namespace osu.Game.Tests.Visual.Gameplay
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
- public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
+ public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException();
+
+ public event Action SourceChanged;
}
private class TestSkinComponent : ISkinComponent
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs
index f2718b8e80..13116de320 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs
@@ -10,14 +10,9 @@ namespace osu.Game.Tests.Visual.Menus
{
public class TestSceneDisclaimer : ScreenTestScene
{
- [Cached(typeof(IAPIProvider))]
- private readonly DummyAPIAccess api = new DummyAPIAccess();
-
[BackgroundDependencyLoader]
- private void load()
+ private void load(IAPIProvider api)
{
- Add(api);
-
AddStep("load disclaimer", () => LoadScreen(new Disclaimer()));
AddStep("toggle support", () =>
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs
index fa3c392b2e..723e5fc03d 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs
@@ -14,6 +14,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMatchLeaderboard : MultiplayerTestScene
{
+ protected override bool RequiresAPIAccess => true;
+
public TestSceneMatchLeaderboard()
{
Room.RoomID.Value = 3;
@@ -27,11 +29,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
}
- [Resolved]
- private IAPIProvider api { get; set; }
-
[BackgroundDependencyLoader]
- private void load()
+ private void load(IAPIProvider api)
{
var req = new GetRoomScoresRequest();
req.Success += v => { };
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs
index 069e133c2b..b646433846 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs
@@ -12,6 +12,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
[TestFixture]
public class TestSceneMultiScreen : ScreenTestScene
{
+ protected override bool RequiresAPIAccess => true;
+
public override IReadOnlyList RequiredTypes => new[]
{
typeof(Screens.Multi.Multiplayer),
diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
index 35449f5687..66ab1fe18a 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs
@@ -25,17 +25,14 @@ namespace osu.Game.Tests.Visual.Online
typeof(AccountCreationScreen),
};
- [Cached(typeof(IAPIProvider))]
- private DummyAPIAccess api = new DummyAPIAccess();
+ private readonly Container userPanelArea;
public TestSceneAccountCreationOverlay()
{
- Container userPanelArea;
AccountCreationOverlay accountCreation;
Children = new Drawable[]
{
- api,
accountCreation = new AccountCreationOverlay(),
userPanelArea = new Container
{
@@ -46,11 +43,16 @@ namespace osu.Game.Tests.Visual.Online
},
};
+ AddStep("show", () => accountCreation.Show());
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(IAPIProvider api)
+ {
api.Logout();
api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
- AddStep("show", () => accountCreation.Show());
- AddStep("logout", () => api.Logout());
+ AddStep("logout", api.Logout);
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
index ee9e088dcc..5068064a1f 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
@@ -42,6 +42,8 @@ namespace osu.Game.Tests.Visual.Online
typeof(BeatmapAvailability),
};
+ protected override bool RequiresAPIAccess => true;
+
private RulesetInfo taikoRuleset;
private RulesetInfo maniaRuleset;
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
index cf8bac7642..324291c9d7 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
@@ -27,6 +27,8 @@ namespace osu.Game.Tests.Visual.Online
typeof(Comments),
};
+ protected override bool RequiresAPIAccess => true;
+
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs
index c18e0e3064..a1c77e2db0 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs
@@ -127,6 +127,9 @@ namespace osu.Game.Tests.Visual.Online
addMessageWithChecks("is now playing [https://osu.ppy.sh/b/252238 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmap);
addMessageWithChecks("Let's (try)[https://osu.ppy.sh/home] [https://osu.ppy.sh/b/252238 multiple links] https://osu.ppy.sh/home", 3,
expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External });
+ addMessageWithChecks("[https://osu.ppy.sh/home New link format with escaped [and \\[ paired] braces]", 1, expectedActions: LinkAction.External);
+ addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://osu.ppy.sh/home)", 1, expectedActions: LinkAction.External);
+ addMessageWithChecks("(Old link format with escaped (and \\( paired) parentheses)[https://osu.ppy.sh/home] and [[also a rogue wiki link]]", 2, expectedActions: new[] { LinkAction.External, LinkAction.External });
// note that there's 0 links here (they get removed if a channel is not found)
addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present).");
addMessageWithChecks("I am important!", 0, false, true);
diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs
index 75c2a2a6a1..14ae975806 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs
@@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual.Online
{
private DirectOverlay direct;
+ protected override bool RequiresAPIAccess => true;
+
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
index 838347800f..c98f98c23d 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
@@ -17,14 +17,15 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneHistoricalSection : OsuTestScene
{
- public override IReadOnlyList RequiredTypes =>
- new[]
- {
- typeof(HistoricalSection),
- typeof(PaginatedMostPlayedBeatmapContainer),
- typeof(DrawableMostPlayedBeatmap),
- typeof(DrawableProfileRow)
- };
+ protected override bool RequiresAPIAccess => true;
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(HistoricalSection),
+ typeof(PaginatedMostPlayedBeatmapContainer),
+ typeof(DrawableMostPlayedBeatmap),
+ typeof(DrawableProfileRow)
+ };
public TestSceneHistoricalSection()
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs
index 5cb96c7ed2..806b36e855 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs
@@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneSocialOverlay : OsuTestScene
{
+ protected override bool RequiresAPIAccess => true;
+
public override IReadOnlyList RequiredTypes => new[]
{
typeof(UserPanel),
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
index 2285c9b799..555d5334d8 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
@@ -17,6 +17,8 @@ namespace osu.Game.Tests.Visual.Online
{
public class TestSceneUserProfileHeader : OsuTestScene
{
+ protected override bool RequiresAPIAccess => true;
+
public override IReadOnlyList RequiredTypes => new[]
{
typeof(ProfileHeader),
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
index 84c99d8c3a..42c8ffbf0a 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
@@ -19,6 +19,8 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneUserProfileOverlay : OsuTestScene
{
+ protected override bool RequiresAPIAccess => true;
+
private readonly TestUserProfileOverlay profile;
[Resolved]
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
index 9f0a8c769a..d777f9766a 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
@@ -18,6 +18,8 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneUserRanks : OsuTestScene
{
+ protected override bool RequiresAPIAccess => true;
+
public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) };
public TestSceneUserRanks()
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
index 9cdfcb6cc4..fdc50be3fa 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
@@ -20,6 +20,8 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneUpdateableBeatmapBackgroundSprite : OsuTestScene
{
+ protected override bool RequiresAPIAccess => true;
+
private BeatmapSetInfo testBeatmap;
private IAPIProvider api;
private RulesetStore rulesets;
diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
index 81f517dd86..8014631eca 100644
--- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
+++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
@@ -84,7 +84,7 @@ namespace osu.Game.Beatmaps.Drawables
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
// the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment)
- Icon = ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }
+ Icon = ruleset?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }
}
};
}
diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs
index d5cdd7e4bc..02382cfd2b 100644
--- a/osu.Game/Configuration/DatabasedConfigManager.cs
+++ b/osu.Game/Configuration/DatabasedConfigManager.cs
@@ -16,11 +16,11 @@ namespace osu.Game.Configuration
private readonly int? variant;
- private readonly List databasedSettings;
+ private List databasedSettings;
private readonly RulesetInfo ruleset;
- private readonly bool legacySettingsExist;
+ private bool legacySettingsExist;
protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int? variant = null)
{
@@ -28,21 +28,31 @@ namespace osu.Game.Configuration
this.ruleset = ruleset;
this.variant = variant;
- databasedSettings = settings.Query(ruleset?.ID, variant);
- legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out var _));
+ Load();
InitialiseDefaults();
}
protected override void PerformLoad()
{
+ databasedSettings = settings.Query(ruleset?.ID, variant);
+ legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out var _));
}
protected override bool PerformSave()
{
+ lock (dirtySettings)
+ {
+ foreach (var setting in dirtySettings)
+ settings.Update(setting);
+ dirtySettings.Clear();
+ }
+
return true;
}
+ private readonly List dirtySettings = new List();
+
protected override void AddBindable(T lookup, Bindable bindable)
{
base.AddBindable(lookup, bindable);
@@ -80,7 +90,12 @@ namespace osu.Game.Configuration
bindable.ValueChanged += b =>
{
setting.Value = b.NewValue;
- settings.Update(setting);
+
+ lock (dirtySettings)
+ {
+ if (!dirtySettings.Contains(setting))
+ dirtySettings.Add(setting);
+ }
};
}
}
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 0cecbb225f..185e5d6df8 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation);
- Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1);
+ Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f);
// Online settings
Set(OsuSetting.Username, string.Empty);
@@ -58,8 +58,8 @@ namespace osu.Game.Configuration
Set(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1);
// Input
- Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2, 0.01);
- Set(OsuSetting.GameplayCursorSize, 1.0, 0.1f, 2, 0.01);
+ Set(OsuSetting.MenuCursorSize, 1.0f, 0.5f, 2f, 0.01f);
+ Set(OsuSetting.GameplayCursorSize, 1.0f, 0.1f, 2f, 0.01f);
Set(OsuSetting.AutoCursorSize, false);
Set(OsuSetting.MouseDisableButtons, false);
diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs
index d13475189d..0f923c3a28 100644
--- a/osu.Game/Graphics/Backgrounds/Background.cs
+++ b/osu.Game/Graphics/Backgrounds/Background.cs
@@ -57,8 +57,9 @@ namespace osu.Game.Graphics.Backgrounds
AddInternal(bufferedContainer = new BufferedContainer
{
- CacheDrawnFrameBuffer = true,
RelativeSizeAxes = Axes.Both,
+ CacheDrawnFrameBuffer = true,
+ RedrawOnScale = false,
Child = Sprite
});
}
diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs
index e103798355..5a83d8e4ce 100644
--- a/osu.Game/Graphics/Cursor/MenuCursor.cs
+++ b/osu.Game/Graphics/Cursor/MenuCursor.cs
@@ -124,7 +124,7 @@ namespace osu.Game.Graphics.Cursor
public class Cursor : Container
{
private Container cursorContainer;
- private Bindable cursorScale;
+ private Bindable cursorScale;
private const float base_scale = 0.15f;
public Sprite AdditiveLayer;
@@ -159,9 +159,8 @@ namespace osu.Game.Graphics.Cursor
}
};
- cursorScale = config.GetBindable(OsuSetting.MenuCursorSize);
- cursorScale.ValueChanged += scale => cursorContainer.Scale = new Vector2((float)scale.NewValue * base_scale);
- cursorScale.TriggerChange();
+ cursorScale = config.GetBindable(OsuSetting.MenuCursorSize);
+ cursorScale.BindValueChanged(scale => cursorContainer.Scale = new Vector2(scale.NewValue * base_scale), true);
}
}
diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs
index 24816deeb5..12688da9df 100644
--- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs
+++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs
@@ -55,6 +55,7 @@ namespace osu.Game.Graphics.Sprites
Origin = Anchor.Centre,
BlurSigma = new Vector2(4),
CacheDrawnFrameBuffer = true,
+ RedrawOnScale = false,
RelativeSizeAxes = Axes.Both,
Blending = BlendingParameters.Additive,
Size = new Vector2(3f),
diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs
index f94e0edb8f..42284e17b5 100644
--- a/osu.Game/Online/Chat/MessageFormatter.cs
+++ b/osu.Game/Online/Chat/MessageFormatter.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Text.RegularExpressions;
namespace osu.Game.Online.Chat
@@ -10,16 +11,16 @@ namespace osu.Game.Online.Chat
public static class MessageFormatter
{
// [[Performance Points]] -> wiki:Performance Points (https://osu.ppy.sh/wiki/Performance_Points)
- private static readonly Regex wiki_regex = new Regex(@"\[\[([^\]]+)\]\]");
+ private static readonly Regex wiki_regex = new Regex(@"\[\[(?[^\]]+)\]\]");
// (test)[https://osu.ppy.sh/b/1234] -> test (https://osu.ppy.sh/b/1234)
- private static readonly Regex old_link_regex = new Regex(@"\(([^\)]*)\)\[([a-z]+://[^ ]+)\]");
+ private static readonly Regex old_link_regex = new Regex(@"\((?(((?<=\\)[\(\)])|[^\(\)])*(((?\()(((?<=\\)[\(\)])|[^\(\)])*)+((?\))(((?<=\\)[\(\)])|[^\(\)])*)+)*(?(open)(?!)))\)\[(?[a-z]+://[^ ]+)\]");
// [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234)
- private static readonly Regex new_link_regex = new Regex(@"\[([a-z]+://[^ ]+) ([^\[\]]*(((?\[)[^\[\]]*)+((?\])[^\[\]]*)+)*(?(open)(?!)))\]");
+ private static readonly Regex new_link_regex = new Regex(@"\[(?[a-z]+://[^ ]+) (?(((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]");
// [test](https://osu.ppy.sh/b/1234) -> test (https://osu.ppy.sh/b/1234) aka correct markdown format
- private static readonly Regex markdown_link_regex = new Regex(@"\[([^\]]*)\]\(([a-z]+://[^ ]+)\)");
+ private static readonly Regex markdown_link_regex = new Regex(@"\[(?(((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]\((?[a-z]+://[^ ]+)\)");
// advanced, RFC-compatible regular expression that matches any possible URL, *but* allows certain invalid characters that are widely used
// This is in the format (, [optional]):
@@ -48,7 +49,7 @@ namespace osu.Game.Online.Chat
// Unicode emojis
private static readonly Regex emoji_regex = new Regex(@"(\uD83D[\uDC00-\uDE4F])");
- private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null)
+ private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[] escapeChars = null)
{
int captureOffset = 0;
@@ -58,16 +59,20 @@ namespace osu.Game.Online.Chat
var displayText = string.Format(display,
m.Groups[0],
- m.Groups.Count > 1 ? m.Groups[1].Value : "",
- m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();
+ m.Groups["text"].Value,
+ m.Groups["url"].Value).Trim();
var linkText = string.Format(link,
m.Groups[0],
- m.Groups.Count > 1 ? m.Groups[1].Value : "",
- m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();
+ m.Groups["text"].Value,
+ m.Groups["url"].Value).Trim();
if (displayText.Length == 0 || linkText.Length == 0) continue;
+ // Remove backslash escapes in front of the characters provided in escapeChars
+ if (escapeChars != null)
+ displayText = escapeChars.Aggregate(displayText, (current, c) => current.Replace($"\\{c}", c.ToString()));
+
// Check for encapsulated links
if (result.Links.Find(l => (l.Index <= index && l.Index + l.Length >= index + m.Length) || (index <= l.Index && index + m.Length >= l.Index + l.Length)) == null)
{
@@ -183,13 +188,13 @@ namespace osu.Game.Online.Chat
var result = new MessageFormatterResult(toFormat);
// handle the [link display] format
- handleMatches(new_link_regex, "{2}", "{1}", result, startIndex);
+ handleMatches(new_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' });
// handle the standard markdown []() format
- handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex);
+ handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' });
// handle the ()[] link format
- handleMatches(old_link_regex, "{1}", "{2}", result, startIndex);
+ handleMatches(old_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '(', ')' });
// handle wiki links
handleMatches(wiki_regex, "{1}", "https://osu.ppy.sh/wiki/{1}", result, startIndex);
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index de8f316b06..d6b8ad3e67 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -207,6 +207,7 @@ namespace osu.Game
FileStore.Cleanup();
AddInternal(API);
+ AddInternal(RulesetConfigCache);
GlobalActionContainer globalBinding;
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index 53a05656b1..6f848c7627 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -54,7 +54,7 @@ namespace osu.Game.Overlays
private Box chatBackground;
private Box tabBackground;
- public Bindable ChatHeight { get; set; }
+ public Bindable ChatHeight { get; set; }
private Container channelSelectionContainer;
protected ChannelSelectionOverlay ChannelSelectionOverlay;
@@ -190,14 +190,13 @@ namespace osu.Game.Overlays
ChannelSelectionOverlay.OnRequestJoin = channel => channelManager.JoinChannel(channel);
ChannelSelectionOverlay.OnRequestLeave = channelManager.LeaveChannel;
- ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight);
- ChatHeight.ValueChanged += height =>
+ ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight);
+ ChatHeight.BindValueChanged(height =>
{
- chatContainer.Height = (float)height.NewValue;
- channelSelectionContainer.Height = 1f - (float)height.NewValue;
- tabBackground.FadeTo(height.NewValue == 1 ? 1 : 0.8f, 200);
- };
- ChatHeight.TriggerChange();
+ chatContainer.Height = height.NewValue;
+ channelSelectionContainer.Height = 1f - height.NewValue;
+ tabBackground.FadeTo(height.NewValue == 1f ? 1f : 0.8f, 200);
+ }, true);
chatBackground.Colour = colours.ChatBlue;
@@ -273,7 +272,7 @@ namespace osu.Game.Overlays
}
}
- private double startDragChatHeight;
+ private float startDragChatHeight;
private bool isDragging;
protected override bool OnDragStart(DragStartEvent e)
@@ -291,7 +290,7 @@ namespace osu.Game.Overlays
{
if (isDragging)
{
- double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y;
+ float targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y;
// If the channel selection screen is shown, mind its minimum height
if (ChannelSelectionOverlay.State.Value == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height)
diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs
index a3243a655e..cf42c8005a 100644
--- a/osu.Game/Overlays/NowPlayingOverlay.cs
+++ b/osu.Game/Overlays/NowPlayingOverlay.cs
@@ -346,10 +346,12 @@ namespace osu.Game.Overlays
public Background(WorkingBeatmap beatmap = null)
{
this.beatmap = beatmap;
- CacheDrawnFrameBuffer = true;
+
Depth = float.MaxValue;
RelativeSizeAxes = Axes.Both;
+ CacheDrawnFrameBuffer = true;
+
Children = new Drawable[]
{
sprite = new Sprite
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index 35be930a2e..d3029d8ab9 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -35,16 +35,16 @@ namespace osu.Game.Overlays.Settings.Sections
Children = new Drawable[]
{
skinDropdown = new SkinSettingsDropdown(),
- new SettingsSlider
+ new SettingsSlider
{
LabelText = "Menu cursor size",
- Bindable = config.GetBindable(OsuSetting.MenuCursorSize),
+ Bindable = config.GetBindable(OsuSetting.MenuCursorSize),
KeyboardStep = 0.01f
},
- new SettingsSlider
+ new SettingsSlider
{
LabelText = "Gameplay cursor size",
- Bindable = config.GetBindable(OsuSetting.GameplayCursorSize),
+ Bindable = config.GetBindable(OsuSetting.GameplayCursorSize),
KeyboardStep = 0.01f
},
new SettingsCheckbox
@@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Settings.Sections
}
}
- private class SizeSlider : OsuSliderBar
+ private class SizeSlider : OsuSliderBar
{
public override string TooltipText => Current.Value.ToString(@"0.##x");
}
diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs
index 56dc121b17..59a7917e55 100644
--- a/osu.Game/Rulesets/Judgements/JudgementResult.cs
+++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs
@@ -51,6 +51,11 @@ namespace osu.Game.Rulesets.Judgements
///
public double HealthAtJudgement { get; internal set; }
+ ///
+ /// Whether the user was in a failed state prior to this occurring.
+ ///
+ public bool FailedAtJudgement { get; internal set; }
+
///
/// Whether a miss or hit occurred.
///
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 1a224b2cea..db87d4b4f2 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -76,8 +76,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
///
public JudgementResult Result { get; private set; }
- private bool judgementOccurred;
-
public override bool RemoveWhenNotAlive => false;
public override bool RemoveCompletedTransforms => false;
protected override bool RequiresChildrenUpdate => true;
@@ -164,7 +162,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
UpdateInitialTransforms();
- var judgementOffset = Math.Min(HitObject.HitWindows?.HalfWindowFor(HitResult.Miss) ?? double.MaxValue, Result?.TimeOffset ?? 0);
+ var judgementOffset = Math.Min(HitObject.HitWindows?.WindowFor(HitResult.Miss) ?? double.MaxValue, Result?.TimeOffset ?? 0);
using (BeginDelayedSequence(InitialLifetimeOffset + judgementOffset, true))
{
@@ -241,7 +239,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
- AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
+ {
+ var comboColours = skin.GetConfig>(GlobalSkinConfiguration.ComboColours)?.Value;
+
+ AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White;
+ }
}
///
@@ -342,8 +344,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
if (!Result.HasResult)
throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}.");
- judgementOccurred = true;
-
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
Result.TimeOffset = Time.Current - endTime;
@@ -376,21 +376,13 @@ namespace osu.Game.Rulesets.Objects.Drawables
if (Time.Elapsed < 0)
return false;
- judgementOccurred = false;
-
- if (AllJudged)
+ if (Judged)
return false;
- foreach (var d in NestedHitObjects)
- judgementOccurred |= d.UpdateResult(userTriggered);
-
- if (judgementOccurred || Judged)
- return judgementOccurred;
-
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
CheckForResult(userTriggered, Time.Current - endTime);
- return judgementOccurred;
+ return Judged;
}
///
diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs
index 5e029139d9..96297ab44f 100644
--- a/osu.Game/Rulesets/Objects/HitObject.cs
+++ b/osu.Game/Rulesets/Objects/HitObject.cs
@@ -9,6 +9,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs
index 06fde576d2..609bdd571a 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs
index 096c07f7d2..350ee3185d 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs
index 226d91bb86..e372fbd273 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs
index eb20fa67f1..067377d300 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs
index 84b66a4c26..c9851a0074 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
using osuTK;
namespace osu.Game.Rulesets.Objects.Legacy.Osu
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs
index c850feb189..1c1180702b 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
using osuTK;
namespace osu.Game.Rulesets.Objects.Legacy.Osu
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
index e5a8884aa2..bc94ea1803 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
using osuTK;
namespace osu.Game.Rulesets.Objects.Legacy.Osu
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs
index 5cecc2a59f..709345170f 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Game.Rulesets.Scoring;
+
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
///
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs
index 5cedc6e2e5..c173b3e11a 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Game.Rulesets.Scoring;
+
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
///
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs
index ca9fdd53ed..9a35ad2776 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index b63292757d..197c089f71 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -7,6 +7,7 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
+using osu.Framework.IO.Stores;
using osu.Game.Beatmaps;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Edit;
@@ -83,6 +84,8 @@ namespace osu.Game.Rulesets
public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle };
+ public virtual IResourceStore CreateReourceStore() => new NamespacedResourceStore(new DllResourceStore(GetType().Assembly.Location), @"Resources");
+
public abstract string Description { get; }
public virtual RulesetSettingsSubsection CreateSettings() => null;
diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs
index 8c9e3c94e2..cdcd2666cf 100644
--- a/osu.Game/Rulesets/RulesetConfigCache.cs
+++ b/osu.Game/Rulesets/RulesetConfigCache.cs
@@ -36,5 +36,14 @@ namespace osu.Game.Rulesets
return configCache.GetOrAdd(ruleset.RulesetInfo.ID.Value, _ => ruleset.CreateConfig(settingsStore));
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ // ensures any potential database operations are finalised before game destruction.
+ foreach (var c in configCache.Values)
+ (c as IDisposable)?.Dispose();
+ }
}
}
diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs
index d9cff86265..6a69fd8dd0 100644
--- a/osu.Game/Rulesets/RulesetInfo.cs
+++ b/osu.Game/Rulesets/RulesetInfo.cs
@@ -20,7 +20,12 @@ namespace osu.Game.Rulesets
[JsonIgnore]
public bool Available { get; set; }
- public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this);
+ public virtual Ruleset CreateInstance()
+ {
+ if (!Available) return null;
+
+ return (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this);
+ }
public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo;
diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs
index 2376f12e9e..7ba88d3df8 100644
--- a/osu.Game/Rulesets/Scoring/HitResult.cs
+++ b/osu.Game/Rulesets/Scoring/HitResult.cs
@@ -16,6 +16,10 @@ namespace osu.Game.Rulesets.Scoring
///
/// Indicates that the object has been judged as a miss.
///
+ ///
+ /// This miss window should determine how early a hit can be before it is considered for judgement (as opposed to being ignored as
+ /// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time).
+ ///
[Description(@"Miss")]
Miss,
diff --git a/osu.Game/Rulesets/Objects/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs
similarity index 55%
rename from osu.Game/Rulesets/Objects/HitWindows.cs
rename to osu.Game/Rulesets/Scoring/HitWindows.cs
index e88af67c7c..efc4cd9f5c 100644
--- a/osu.Game/Rulesets/Objects/HitWindows.cs
+++ b/osu.Game/Rulesets/Scoring/HitWindows.cs
@@ -4,51 +4,31 @@
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.Objects;
-namespace osu.Game.Rulesets.Objects
+namespace osu.Game.Rulesets.Scoring
{
+ ///
+ /// A structure containing timing data for hit window based gameplay.
+ ///
public class HitWindows
{
- private static readonly IReadOnlyDictionary base_ranges = new Dictionary
+ private static readonly DifficultyRange[] base_ranges =
{
- { HitResult.Perfect, (44.8, 38.8, 27.8) },
- { HitResult.Great, (128, 98, 68) },
- { HitResult.Good, (194, 164, 134) },
- { HitResult.Ok, (254, 224, 194) },
- { HitResult.Meh, (302, 272, 242) },
- { HitResult.Miss, (376, 346, 316) },
+ new DifficultyRange(HitResult.Perfect, 22.4D, 19.4D, 13.9D),
+ new DifficultyRange(HitResult.Great, 64, 49, 34),
+ new DifficultyRange(HitResult.Good, 97, 82, 67),
+ new DifficultyRange(HitResult.Ok, 127, 112, 97),
+ new DifficultyRange(HitResult.Meh, 151, 136, 121),
+ new DifficultyRange(HitResult.Miss, 188, 173, 158),
};
- ///
- /// Hit window for a result.
- ///
- public double Perfect { get; protected set; }
-
- ///
- /// Hit window for a result.
- ///
- public double Great { get; protected set; }
-
- ///
- /// Hit window for a result.
- ///
- public double Good { get; protected set; }
-
- ///
- /// Hit window for an result.
- ///
- public double Ok { get; protected set; }
-
- ///
- /// Hit window for a result.
- ///
- public double Meh { get; protected set; }
-
- ///
- /// Hit window for a result.
- ///
- public double Miss { get; protected set; }
+ private double perfect;
+ private double great;
+ private double good;
+ private double ok;
+ private double meh;
+ private double miss;
///
/// Retrieves the with the largest hit window that produces a successful hit.
@@ -66,15 +46,15 @@ namespace osu.Game.Rulesets.Objects
}
///
- /// Retrieves a mapping of s to their half window timing for all allowed s.
+ /// Retrieves a mapping of s to their timing windows for all allowed s.
///
///
- public IEnumerable<(HitResult result, double length)> GetAllAvailableHalfWindows()
+ public IEnumerable<(HitResult result, double length)> GetAllAvailableWindows()
{
for (var result = HitResult.Meh; result <= HitResult.Perfect; ++result)
{
if (IsHitResultAllowed(result))
- yield return (result, HalfWindowFor(result));
+ yield return (result, WindowFor(result));
}
}
@@ -83,31 +63,45 @@ namespace osu.Game.Rulesets.Objects
///
/// The result type to check.
/// Whether the can be achieved.
- public virtual bool IsHitResultAllowed(HitResult result)
- {
- switch (result)
- {
- case HitResult.Perfect:
- case HitResult.Ok:
- return false;
-
- default:
- return true;
- }
- }
+ public virtual bool IsHitResultAllowed(HitResult result) => true;
///
/// Sets hit windows with values that correspond to a difficulty parameter.
///
/// The parameter.
- public virtual void SetDifficulty(double difficulty)
+ public void SetDifficulty(double difficulty)
{
- Perfect = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Perfect]);
- Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]);
- Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]);
- Ok = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Ok]);
- Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]);
- Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]);
+ foreach (var range in GetRanges())
+ {
+ var value = BeatmapDifficulty.DifficultyRange(difficulty, (range.Min, range.Average, range.Max));
+
+ switch (range.Result)
+ {
+ case HitResult.Miss:
+ miss = value;
+ break;
+
+ case HitResult.Meh:
+ meh = value;
+ break;
+
+ case HitResult.Ok:
+ ok = value;
+ break;
+
+ case HitResult.Good:
+ good = value;
+ break;
+
+ case HitResult.Great:
+ great = value;
+ break;
+
+ case HitResult.Perfect:
+ perfect = value;
+ break;
+ }
+ }
}
///
@@ -121,7 +115,7 @@ namespace osu.Game.Rulesets.Objects
for (var result = HitResult.Perfect; result >= HitResult.Miss; --result)
{
- if (IsHitResultAllowed(result) && timeOffset <= HalfWindowFor(result))
+ if (IsHitResultAllowed(result) && timeOffset <= WindowFor(result))
return result;
}
@@ -129,32 +123,32 @@ namespace osu.Game.Rulesets.Objects
}
///
- /// Retrieves half the hit window for a .
- /// This is useful if the hit window for one half of the hittable range of a is required.
+ /// Retrieves the hit window for a .
+ /// This is the number of +/- milliseconds allowed for the requested result (so the actual hittable range is double this).
///
/// The expected .
/// One half of the hit window for .
- public double HalfWindowFor(HitResult result)
+ public double WindowFor(HitResult result)
{
switch (result)
{
case HitResult.Perfect:
- return Perfect / 2;
+ return perfect;
case HitResult.Great:
- return Great / 2;
+ return great;
case HitResult.Good:
- return Good / 2;
+ return good;
case HitResult.Ok:
- return Ok / 2;
+ return ok;
case HitResult.Meh:
- return Meh / 2;
+ return meh;
case HitResult.Miss:
- return Miss / 2;
+ return miss;
default:
throw new ArgumentException(nameof(result));
@@ -167,6 +161,30 @@ namespace osu.Game.Rulesets.Objects
///
/// The time offset.
/// Whether the can be hit at any point in the future from this time offset.
- public bool CanBeHit(double timeOffset) => timeOffset <= HalfWindowFor(LowestSuccessfulHitResult());
+ public bool CanBeHit(double timeOffset) => timeOffset <= WindowFor(LowestSuccessfulHitResult());
+
+ ///
+ /// Retrieve a valid list of s representing hit windows.
+ /// Defaults are provided but can be overridden to customise for a ruleset.
+ ///
+ protected virtual DifficultyRange[] GetRanges() => base_ranges;
+ }
+
+ public struct DifficultyRange
+ {
+ public readonly HitResult Result;
+
+ public double Min;
+ public double Average;
+ public double Max;
+
+ public DifficultyRange(HitResult result, double min, double average, double max)
+ {
+ Result = result;
+
+ Min = min;
+ Average = average;
+ Max = max;
+ }
}
}
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 86c2c07f2a..e4f20c27b4 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -92,6 +92,11 @@ namespace osu.Game.Rulesets.Scoring
///
public virtual bool HasCompleted => false;
+ ///
+ /// The total number of judged s at the current point in time.
+ ///
+ public int JudgedHits { get; protected set; }
+
///
/// Whether this ScoreProcessor has already triggered the failed state.
///
@@ -142,6 +147,8 @@ namespace osu.Game.Rulesets.Scoring
Rank.Value = ScoreRank.X;
HighestCombo.Value = 0;
+ JudgedHits = 0;
+
HasFailed = false;
}
@@ -193,7 +200,7 @@ namespace osu.Game.Rulesets.Scoring
score.Statistics[result] = GetStatistic(result);
}
- protected abstract int GetStatistic(HitResult result);
+ public abstract int GetStatistic(HitResult result);
public abstract double GetStandardisedScore();
}
@@ -208,7 +215,6 @@ namespace osu.Game.Rulesets.Scoring
public sealed override bool HasCompleted => JudgedHits == MaxHits;
protected int MaxHits { get; private set; }
- protected int JudgedHits { get; private set; }
private double maxHighestCombo;
@@ -322,6 +328,10 @@ namespace osu.Game.Rulesets.Scoring
result.ComboAtJudgement = Combo.Value;
result.HighestComboAtJudgement = HighestCombo.Value;
result.HealthAtJudgement = Health.Value;
+ result.FailedAtJudgement = HasFailed;
+
+ if (HasFailed)
+ return;
JudgedHits++;
@@ -369,6 +379,11 @@ namespace osu.Game.Rulesets.Scoring
HighestCombo.Value = result.HighestComboAtJudgement;
Health.Value = result.HealthAtJudgement;
+ // Todo: Revert HasFailed state with proper player support
+
+ if (result.FailedAtJudgement)
+ return;
+
JudgedHits--;
if (result.Judgement.IsBonus)
@@ -415,7 +430,7 @@ namespace osu.Game.Rulesets.Scoring
}
}
- protected override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result);
+ public override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result);
public override double GetStandardisedScore() => getScore(ScoringMode.Standardised);
@@ -432,7 +447,6 @@ namespace osu.Game.Rulesets.Scoring
base.Reset(storeResults);
- JudgedHits = 0;
baseScore = 0;
rollingMaxBaseScore = 0;
bonusScore = 0;
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index a32407d180..a34bb6e8ea 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -11,13 +11,20 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using JetBrains.Annotations;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.Input.Events;
+using osu.Framework.IO.Stores;
using osu.Game.Configuration;
using osu.Game.Graphics.Cursor;
using osu.Game.Input.Handlers;
@@ -51,6 +58,10 @@ namespace osu.Game.Rulesets.UI
private readonly Lazy playfield;
+ private TextureStore textureStore;
+
+ private ISampleStore localSampleStore;
+
///
/// The playfield.
///
@@ -142,6 +153,18 @@ namespace osu.Game.Rulesets.UI
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+ var resources = Ruleset.CreateReourceStore();
+
+ if (resources != null)
+ {
+ textureStore = new TextureStore(new TextureLoaderStore(new NamespacedResourceStore(resources, "Textures")));
+ textureStore.AddStore(dependencies.Get());
+ dependencies.Cache(textureStore);
+
+ localSampleStore = dependencies.Get().GetSampleStore(new NamespacedResourceStore(resources, "Samples"));
+ dependencies.CacheAs(new FallbackSampleStore(localSampleStore, dependencies.Get()));
+ }
+
onScreenDisplay = dependencies.Get();
Config = dependencies.Get().GetConfigFor(Ruleset);
@@ -314,6 +337,8 @@ namespace osu.Game.Rulesets.UI
{
base.Dispose(isDisposing);
+ localSampleStore?.Dispose();
+
if (Config != null)
{
onScreenDisplay?.StopTracking(this, Config);
@@ -443,4 +468,50 @@ namespace osu.Game.Rulesets.UI
{
}
}
+
+ ///
+ /// A sample store which adds a fallback source.
+ ///
+ ///
+ /// This is a temporary implementation to workaround ISampleStore limitations.
+ ///
+ public class FallbackSampleStore : ISampleStore
+ {
+ private readonly ISampleStore primary;
+ private readonly ISampleStore secondary;
+
+ public FallbackSampleStore(ISampleStore primary, ISampleStore secondary)
+ {
+ this.primary = primary;
+ this.secondary = secondary;
+ }
+
+ public SampleChannel Get(string name) => primary.Get(name) ?? secondary.Get(name);
+
+ public Task GetAsync(string name) => primary.GetAsync(name) ?? secondary.GetAsync(name);
+
+ public Stream GetStream(string name) => primary.GetStream(name) ?? secondary.GetStream(name);
+
+ public IEnumerable GetAvailableResources() => throw new NotImplementedException();
+
+ public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException();
+
+ public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException();
+
+ public BindableDouble Volume => throw new NotImplementedException();
+
+ public BindableDouble Balance => throw new NotImplementedException();
+
+ public BindableDouble Frequency => throw new NotImplementedException();
+
+ public int PlaybackConcurrency
+ {
+ get => throw new NotImplementedException();
+ set => throw new NotImplementedException();
+ }
+
+ public void Dispose()
+ {
+ }
+ }
}
diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs
index 266725a739..d3c37bd4f4 100644
--- a/osu.Game/Scoring/ScoreInfo.cs
+++ b/osu.Game/Scoring/ScoreInfo.cs
@@ -11,8 +11,8 @@ using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
-using osu.Game.Users;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Users;
namespace osu.Game.Scoring
{
diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs
index 6984959e9c..59ab6ad265 100644
--- a/osu.Game/Screens/Menu/LogoVisualisation.cs
+++ b/osu.Game/Screens/Menu/LogoVisualisation.cs
@@ -122,7 +122,7 @@ namespace osu.Game.Screens.Menu
Color4 defaultColour = Color4.White.Opacity(0.2f);
if (user.Value?.IsSupporter ?? false)
- AccentColour = skin.Value.GetValue(s => s.CustomColours.ContainsKey("MenuGlow") ? s.CustomColours["MenuGlow"] : (Color4?)null) ?? defaultColour;
+ AccentColour = skin.Value.GetConfig(GlobalSkinColour.MenuGlow)?.Value ?? defaultColour;
else
AccentColour = defaultColour;
}
diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs
index 393964561c..55a6a33e89 100644
--- a/osu.Game/Screens/Menu/MenuSideFlashes.cs
+++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs
@@ -112,7 +112,7 @@ namespace osu.Game.Screens.Menu
Color4 baseColour = colours.Blue;
if (user.Value?.IsSupporter ?? false)
- baseColour = skin.Value.GetValue(s => s.CustomColours.ContainsKey("MenuGlow") ? s.CustomColours["MenuGlow"] : (Color4?)null) ?? baseColour;
+ baseColour = skin.Value.GetConfig(GlobalSkinColour.MenuGlow)?.Value ?? baseColour;
// linear colour looks better in this case, so let's use it for now.
Color4 gradientDark = baseColour.Opacity(0).ToLinear();
diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
index f93d5d8b02..c5202fa792 100644
--- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs
+++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
@@ -304,6 +304,9 @@ namespace osu.Game.Screens.Play
private class Button : DialogButton
{
+ // required to ensure keyboard navigation always starts from an extremity (unless the cursor is moved)
+ protected override bool OnHover(HoverEvent e) => true;
+
protected override bool OnMouseMove(MouseMoveEvent e)
{
Selected.Value = true;
diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs
index adda94d629..920d11c910 100644
--- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs
+++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs
@@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
index 594dd64e52..03a0f23fb6 100644
--- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
+++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
@@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osuTK;
using osuTK.Graphics;
@@ -146,7 +145,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
private void createColourBars(OsuColour colours)
{
- var windows = HitWindows.GetAllAvailableHalfWindows().ToArray();
+ var windows = HitWindows.GetAllAvailableWindows().ToArray();
maxHitWindow = windows.First().length;
diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs
index da1d9fff0d..dee25048ed 100644
--- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs
+++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs
@@ -3,7 +3,7 @@
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Play.HUD.HitErrorMeters
{
diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs
index 9c56725c4e..05f6128ac2 100644
--- a/osu.Game/Screens/Play/SquareGraph.cs
+++ b/osu.Game/Screens/Play/SquareGraph.cs
@@ -103,6 +103,7 @@ namespace osu.Game.Screens.Play
var newColumns = new BufferedContainer
{
CacheDrawnFrameBuffer = true,
+ RedrawOnScale = false,
RelativeSizeAxes = Axes.Both,
};
diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
index 5f6307e3b4..65ecd7b812 100644
--- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs
+++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
@@ -154,6 +154,8 @@ namespace osu.Game.Screens.Select
var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
CacheDrawnFrameBuffer = true;
+ RedrawOnScale = false;
+
RelativeSizeAxes = Axes.Both;
titleBinding = localisation.GetLocalisedString(new LocalisedString((metadata.TitleUnicode, metadata.Title)));
diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
index 97b6a78804..699e01bca7 100644
--- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
+++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
@@ -146,6 +146,7 @@ namespace osu.Game.Screens.Select.Carousel
public PanelBackground(WorkingBeatmap working)
{
CacheDrawnFrameBuffer = true;
+ RedrawOnScale = false;
Children = new Drawable[]
{
diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs
index b35c9c7b97..98f158c725 100644
--- a/osu.Game/Skinning/DefaultLegacySkin.cs
+++ b/osu.Game/Skinning/DefaultLegacySkin.cs
@@ -3,6 +3,7 @@
using osu.Framework.Audio;
using osu.Framework.IO.Stores;
+using osuTK.Graphics;
namespace osu.Game.Skinning
{
@@ -11,6 +12,7 @@ namespace osu.Game.Skinning
public DefaultLegacySkin(IResourceStore storage, AudioManager audioManager)
: base(Info, storage, audioManager, string.Empty)
{
+ Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255);
}
public static SkinInfo Info { get; } = new SkinInfo
diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs
index 9eda5d597a..529c1afca5 100644
--- a/osu.Game/Skinning/DefaultSkin.cs
+++ b/osu.Game/Skinning/DefaultSkin.cs
@@ -1,10 +1,13 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
+using osuTK.Graphics;
namespace osu.Game.Skinning
{
@@ -21,5 +24,24 @@ namespace osu.Game.Skinning
public override Texture GetTexture(string componentName) => null;
public override SampleChannel GetSample(ISampleInfo sampleInfo) => null;
+
+ public override IBindable GetConfig(TLookup lookup)
+ {
+ switch (lookup)
+ {
+ // todo: this code is pulled from LegacySkin and should not exist.
+ // will likely change based on how databased storage of skin configuration goes.
+ case GlobalSkinConfiguration global:
+ switch (global)
+ {
+ case GlobalSkinConfiguration.ComboColours:
+ return SkinUtils.As(new Bindable>(Configuration.ComboColours));
+ }
+
+ break;
+ }
+
+ return null;
+ }
}
}
diff --git a/osu.Game/Skinning/DefaultSkinConfiguration.cs b/osu.Game/Skinning/DefaultSkinConfiguration.cs
index 722b35f102..f52fac6077 100644
--- a/osu.Game/Skinning/DefaultSkinConfiguration.cs
+++ b/osu.Game/Skinning/DefaultSkinConfiguration.cs
@@ -12,8 +12,6 @@ namespace osu.Game.Skinning
{
public DefaultSkinConfiguration()
{
- HitCircleFont = "default";
-
ComboColours.AddRange(new[]
{
new Color4(17, 136, 170, 255),
@@ -21,8 +19,6 @@ namespace osu.Game.Skinning
new Color4(204, 102, 0, 255),
new Color4(121, 9, 13, 255)
});
-
- CursorExpand = true;
}
}
}
diff --git a/osu.Game/Skinning/GameplaySkinComponent.cs b/osu.Game/Skinning/GameplaySkinComponent.cs
index 8695b3d720..2aa380fa90 100644
--- a/osu.Game/Skinning/GameplaySkinComponent.cs
+++ b/osu.Game/Skinning/GameplaySkinComponent.cs
@@ -5,7 +5,7 @@ using System.Linq;
namespace osu.Game.Skinning
{
- public class GameplaySkinComponent : ISkinComponent where T : struct
+ public class GameplaySkinComponent : ISkinComponent
{
public readonly T Component;
diff --git a/osu.Game/Skinning/GlobalSkinColour.cs b/osu.Game/Skinning/GlobalSkinColour.cs
new file mode 100644
index 0000000000..d039be98ce
--- /dev/null
+++ b/osu.Game/Skinning/GlobalSkinColour.cs
@@ -0,0 +1,10 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Skinning
+{
+ public enum GlobalSkinColour
+ {
+ MenuGlow
+ }
+}
diff --git a/osu.Game/Skinning/GlobalSkinConfiguration.cs b/osu.Game/Skinning/GlobalSkinConfiguration.cs
new file mode 100644
index 0000000000..66dc9a9395
--- /dev/null
+++ b/osu.Game/Skinning/GlobalSkinConfiguration.cs
@@ -0,0 +1,10 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Skinning
+{
+ public enum GlobalSkinConfiguration
+ {
+ ComboColours
+ }
+}
diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs
index bc1ae634c9..cb2a379b8e 100644
--- a/osu.Game/Skinning/ISkin.cs
+++ b/osu.Game/Skinning/ISkin.cs
@@ -1,8 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
+using JetBrains.Annotations;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
@@ -14,12 +15,36 @@ namespace osu.Game.Skinning
///
public interface ISkin
{
+ ///
+ /// Retrieve a component implementation.
+ ///
+ /// The requested component.
+ /// A drawable representation for the requested component, or null if unavailable.
+ [CanBeNull]
Drawable GetDrawableComponent(ISkinComponent component);
+ ///
+ /// Retrieve a .
+ ///
+ /// The requested texture.
+ /// A matching texture, or null if unavailable.
+ [CanBeNull]
Texture GetTexture(string componentName);
+ ///
+ /// Retrieve a .
+ ///
+ /// The requested sample.
+ /// A matching sample channel, or null if unavailable.
+ [CanBeNull]
SampleChannel GetSample(ISampleInfo sampleInfo);
- TValue GetValue(Func query) where TConfiguration : SkinConfiguration;
+ ///
+ /// Retrieve a configuration value.
+ ///
+ /// The requested configuration value.
+ /// A matching value boxed in an , or null if unavailable.
+ [CanBeNull]
+ IBindable GetConfig(TLookup lookup);
}
}
diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs
index 535471f455..0b1076be01 100644
--- a/osu.Game/Skinning/LegacySkin.cs
+++ b/osu.Game/Skinning/LegacySkin.cs
@@ -1,10 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
using System.IO;
using System.Linq;
+using JetBrains.Annotations;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
@@ -16,29 +19,32 @@ namespace osu.Game.Skinning
{
public class LegacySkin : Skin
{
+ [CanBeNull]
protected TextureStore Textures;
+ [CanBeNull]
protected IResourceStore Samples;
public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager)
: this(skin, new LegacySkinResourceStore(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);
}
protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename)
: base(skin)
{
- Stream stream = storage.GetStream(filename);
+ Stream stream = storage?.GetStream(filename);
if (stream != null)
using (StreamReader reader = new StreamReader(stream))
Configuration = new LegacySkinDecoder().Decode(reader);
else
Configuration = new DefaultSkinConfiguration();
- Samples = audioManager.GetSampleStore(storage);
- Textures = new TextureStore(new TextureLoaderStore(storage));
+ if (storage != null)
+ {
+ Samples = audioManager?.GetSampleStore(storage);
+ Textures = new TextureStore(new TextureLoaderStore(storage));
+ }
}
protected override void Dispose(bool isDisposing)
@@ -48,6 +54,52 @@ namespace osu.Game.Skinning
Samples?.Dispose();
}
+ public override IBindable GetConfig(TLookup lookup)
+ {
+ switch (lookup)
+ {
+ case GlobalSkinConfiguration global:
+ switch (global)
+ {
+ case GlobalSkinConfiguration.ComboColours:
+ return SkinUtils.As(new Bindable>(Configuration.ComboColours));
+ }
+
+ break;
+
+ case GlobalSkinColour colour:
+ return SkinUtils.As(getCustomColour(colour.ToString()));
+
+ case SkinCustomColourLookup customColour:
+ return SkinUtils.As(getCustomColour(customColour.Lookup.ToString()));
+
+ default:
+ try
+ {
+ if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out var val))
+ {
+ // special case for handling skins which use 1 or 0 to signify a boolean state.
+ if (typeof(TValue) == typeof(bool))
+ val = val == "1" ? "true" : "false";
+
+ var bindable = new Bindable();
+ if (val != null)
+ bindable.Parse(val);
+ return bindable;
+ }
+ }
+ catch
+ {
+ }
+
+ break;
+ }
+
+ return null;
+ }
+
+ private IBindable getCustomColour(string lookup) => Configuration.CustomColours.TryGetValue(lookup, out var col) ? new Bindable(col) : null;
+
public override Drawable GetDrawableComponent(ISkinComponent component)
{
switch (component)
@@ -79,12 +131,12 @@ namespace osu.Game.Skinning
componentName = getFallbackName(componentName);
float ratio = 2;
- var texture = Textures.Get($"{componentName}@2x");
+ var texture = Textures?.Get($"{componentName}@2x");
if (texture == null)
{
ratio = 1;
- texture = Textures.Get(componentName);
+ texture = Textures?.Get(componentName);
}
if (texture != null)
@@ -97,7 +149,7 @@ namespace osu.Game.Skinning
{
foreach (var lookup in sampleInfo.LookupNames)
{
- var sample = Samples.Get(getFallbackName(lookup));
+ var sample = Samples?.Get(getFallbackName(lookup));
if (sample != null)
return sample;
@@ -105,7 +157,7 @@ namespace osu.Game.Skinning
if (sampleInfo is HitSampleInfo hsi)
// Try fallback to non-bank samples.
- return Samples.Get(hsi.Name);
+ return Samples?.Get(hsi.Name);
return null;
}
diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs
index 0160755eed..e97664e75e 100644
--- a/osu.Game/Skinning/LegacySkinDecoder.cs
+++ b/osu.Game/Skinning/LegacySkinDecoder.cs
@@ -14,47 +14,31 @@ namespace osu.Game.Skinning
protected override void ParseLine(DefaultSkinConfiguration skin, Section section, string line)
{
- line = StripComments(line);
-
- var pair = SplitKeyVal(line);
-
- switch (section)
+ if (section != Section.Colours)
{
- case Section.General:
- switch (pair.Key)
- {
- case @"Name":
- skin.SkinInfo.Name = pair.Value;
- break;
+ line = StripComments(line);
- case @"Author":
- skin.SkinInfo.Creator = pair.Value;
- break;
+ var pair = SplitKeyVal(line);
- case @"CursorExpand":
- skin.CursorExpand = pair.Value != "0";
- break;
+ switch (section)
+ {
+ case Section.General:
+ switch (pair.Key)
+ {
+ case @"Name":
+ skin.SkinInfo.Name = pair.Value;
+ return;
- case @"SliderBorderSize":
- skin.SliderBorderSize = Parsing.ParseFloat(pair.Value);
- break;
- }
+ case @"Author":
+ skin.SkinInfo.Creator = pair.Value;
+ return;
+ }
- break;
+ break;
+ }
- case Section.Fonts:
- switch (pair.Key)
- {
- case "HitCirclePrefix":
- skin.HitCircleFont = pair.Value;
- break;
-
- case "HitCircleOverlap":
- skin.HitCircleOverlap = int.Parse(pair.Value);
- break;
- }
-
- break;
+ if (!string.IsNullOrEmpty(pair.Key))
+ skin.ConfigDictionary[pair.Key] = pair.Value;
}
base.ParseLine(skin, section, line);
diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs
index 299f257e57..fa4aebd8a5 100644
--- a/osu.Game/Skinning/Skin.cs
+++ b/osu.Game/Skinning/Skin.cs
@@ -1,8 +1,9 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
@@ -13,7 +14,7 @@ namespace osu.Game.Skinning
{
public readonly SkinInfo SkinInfo;
- public virtual SkinConfiguration Configuration { get; protected set; }
+ public SkinConfiguration Configuration { get; protected set; }
public abstract Drawable GetDrawableComponent(ISkinComponent componentName);
@@ -21,8 +22,7 @@ namespace osu.Game.Skinning
public abstract Texture GetTexture(string componentName);
- public TValue GetValue(Func query) where TConfiguration : SkinConfiguration
- => Configuration is TConfiguration conf ? query.Invoke(conf) : default;
+ public abstract IBindable GetConfig(TLookup lookup);
protected Skin(SkinInfo skin)
{
diff --git a/osu.Game/Skinning/SkinConfigManager.cs b/osu.Game/Skinning/SkinConfigManager.cs
new file mode 100644
index 0000000000..896444d1d2
--- /dev/null
+++ b/osu.Game/Skinning/SkinConfigManager.cs
@@ -0,0 +1,16 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Configuration;
+
+namespace osu.Game.Skinning
+{
+ public class SkinConfigManager : ConfigManager where T : struct
+ {
+ protected override void PerformLoad()
+ {
+ }
+
+ protected override bool PerformSave() => false;
+ }
+}
diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs
index d585c58ef1..54aac86e3c 100644
--- a/osu.Game/Skinning/SkinConfiguration.cs
+++ b/osu.Game/Skinning/SkinConfiguration.cs
@@ -18,14 +18,6 @@ namespace osu.Game.Skinning
public Dictionary CustomColours { get; set; } = new Dictionary();
- public string HitCircleFont { get; set; }
-
- public int HitCircleOverlap { get; set; }
-
- public float? SliderBorderSize { get; set; }
-
- public float? SliderPathRadius { get; set; }
-
- public bool? CursorExpand { get; set; }
+ public readonly Dictionary ConfigDictionary = new Dictionary();
}
}
diff --git a/osu.Game/Skinning/SkinCustomColourLookup.cs b/osu.Game/Skinning/SkinCustomColourLookup.cs
new file mode 100644
index 0000000000..b8e5ac9b53
--- /dev/null
+++ b/osu.Game/Skinning/SkinCustomColourLookup.cs
@@ -0,0 +1,15 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Skinning
+{
+ public class SkinCustomColourLookup
+ {
+ public readonly object Lookup;
+
+ public SkinCustomColourLookup(object lookup)
+ {
+ Lookup = lookup;
+ }
+ }
+}
diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs
index a55a128dff..aa3b3981c2 100644
--- a/osu.Game/Skinning/SkinManager.cs
+++ b/osu.Game/Skinning/SkinManager.cs
@@ -1,4 +1,4 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
@@ -131,6 +131,6 @@ namespace osu.Game.Skinning
public SampleChannel GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo);
- public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => CurrentSkin.Value.GetValue(query);
+ public IBindable GetConfig(TLookup lookup) => CurrentSkin.Value.GetConfig(lookup);
}
}
diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs
index 85a80655ea..ef7f5f381b 100644
--- a/osu.Game/Skinning/SkinProvidingContainer.cs
+++ b/osu.Game/Skinning/SkinProvidingContainer.cs
@@ -4,6 +4,7 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
@@ -64,13 +65,16 @@ namespace osu.Game.Skinning
return fallbackSource?.GetSample(sampleInfo);
}
- public TValue GetValue(Func query) where TConfiguration : SkinConfiguration
+ public IBindable GetConfig(TLookup lookup)
{
- TValue val;
- if (AllowConfigurationLookup && skin != null && (val = skin.GetValue(query)) != null)
- return val;
+ if (AllowConfigurationLookup && skin != null)
+ {
+ var bindable = skin.GetConfig(lookup);
+ if (bindable != null)
+ return bindable;
+ }
- return fallbackSource == null ? default : fallbackSource.GetValue(query);
+ return fallbackSource?.GetConfig(lookup);
}
protected virtual void TriggerSourceChanged() => SourceChanged?.Invoke();
diff --git a/osu.Game/Skinning/SkinUtils.cs b/osu.Game/Skinning/SkinUtils.cs
new file mode 100644
index 0000000000..e3bc5e28b8
--- /dev/null
+++ b/osu.Game/Skinning/SkinUtils.cs
@@ -0,0 +1,21 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Bindables;
+
+namespace osu.Game.Skinning
+{
+ ///
+ /// Contains helper methods to assist in implementing s.
+ ///
+ public static class SkinUtils
+ {
+ ///
+ /// Converts an to a . Used for returning configuration values of specific types.
+ ///
+ /// The value.
+ /// The type of value , and the type of the resulting bindable.
+ /// The resulting bindable.
+ public static Bindable As(object value) => (Bindable)value;
+ }
+}
diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs
index 3d0219ed93..bdf8be773b 100644
--- a/osu.Game/Skinning/SkinnableSound.cs
+++ b/osu.Game/Skinning/SkinnableSound.cs
@@ -6,6 +6,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
+using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Audio;
@@ -20,7 +21,7 @@ namespace osu.Game.Skinning
private SampleChannel[] channels;
- private AudioManager audio;
+ private ISampleStore samples;
public SkinnableSound(IEnumerable hitSamples)
{
@@ -33,9 +34,9 @@ namespace osu.Game.Skinning
}
[BackgroundDependencyLoader]
- private void load(AudioManager audio)
+ private void load(ISampleStore samples)
{
- this.audio = audio;
+ this.samples = samples;
}
private bool looping;
@@ -81,7 +82,7 @@ namespace osu.Game.Skinning
if (ch == null && allowFallback)
foreach (var lookup in s.LookupNames)
- if ((ch = audio.Samples.Get($"Gameplay/{lookup}")) != null)
+ if ((ch = samples.Get($"Gameplay/{lookup}")) != null)
break;
if (ch != null)
@@ -102,8 +103,9 @@ namespace osu.Game.Skinning
{
base.Dispose(isDisposing);
- foreach (var c in channels)
- c.Dispose();
+ if (channels != null)
+ foreach (var c in channels)
+ c.Dispose();
}
}
}
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs
index b04f1d4518..f3f8308964 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs
@@ -64,5 +64,11 @@ namespace osu.Game.Storyboards.Drawables
LifetimeEnd = sampleInfo.StartTime;
}
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ channel?.Stop();
+ base.Dispose(isDisposing);
+ }
}
}
diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs
index 27d72f3950..dd68ed93e6 100644
--- a/osu.Game/Tests/Visual/OsuTestScene.cs
+++ b/osu.Game/Tests/Visual/OsuTestScene.cs
@@ -16,6 +16,7 @@ using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Database;
+using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Tests.Beatmaps;
@@ -47,6 +48,12 @@ namespace osu.Game.Tests.Visual
private readonly Lazy contextFactory;
protected DatabaseContextFactory ContextFactory => contextFactory.Value;
+ ///
+ /// Whether this test scene requires API access
+ /// Setting this will cache an actual .
+ ///
+ protected virtual bool RequiresAPIAccess => false;
+
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
// This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures
@@ -57,7 +64,17 @@ namespace osu.Game.Tests.Visual
Default = working
};
- return Dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+ Dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+
+ if (!RequiresAPIAccess)
+ {
+ var dummyAPI = new DummyAPIAccess();
+
+ Dependencies.CacheAs(dummyAPI);
+ Add(dummyAPI);
+ }
+
+ return Dependencies;
}
protected OsuTestScene()
diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs
index ccd996098c..2c5a51ca02 100644
--- a/osu.Game/Tests/Visual/PlayerTestScene.cs
+++ b/osu.Game/Tests/Visual/PlayerTestScene.cs
@@ -50,7 +50,11 @@ namespace osu.Game.Tests.Visual
Beatmap.Value = CreateWorkingBeatmap(beatmap);
if (!AllowFail)
- Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) };
+ {
+ var noFailMod = ruleset.GetAllMods().FirstOrDefault(m => m is ModNoFail);
+ if (noFailMod != null)
+ Mods.Value = new[] { noFailMod };
+ }
if (Autoplay)
{
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 7d106f0484..5f2aad24dc 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -6,16 +6,27 @@
AnyCPU
true
+
+ osu!
+ ppy.osu.Game
+ ppy Pty Ltd
+ https://github.com/ppy/osu/blob/master/LICENCE.md
+ https://github.com/ppy/osu
+ https://github.com/ppy/osu
+ Automated release.
+ Copyright (c) 2019 ppy Pty Ltd
+ osu game
+
-
+
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 8390a2229b..5027a4ef8c 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -113,13 +113,13 @@
-
+
-
-
+
+