diff --git a/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu___legacy_osuTK_.xml b/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu___legacy_osuTK_.xml
deleted file mode 100644
index 9ece926b34..0000000000
--- a/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu___legacy_osuTK_.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/osu.Android.props b/osu.Android.props
index 5b700224db..75ac298626 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index 5909b82c8f..b2487568ce 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -136,24 +136,12 @@ namespace osu.Desktop
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
- switch (host.Window)
- {
- // Legacy osuTK DesktopGameWindow
- case OsuTKDesktopWindow desktopGameWindow:
- desktopGameWindow.CursorState |= CursorState.Hidden;
- desktopGameWindow.SetIconFromStream(iconStream);
- desktopGameWindow.Title = Name;
- desktopGameWindow.FileDrop += (_, e) => fileDrop(e.FileNames);
- break;
+ var desktopWindow = (SDL2DesktopWindow)host.Window;
- // SDL2 DesktopWindow
- case SDL2DesktopWindow desktopWindow:
- desktopWindow.CursorState |= CursorState.Hidden;
- desktopWindow.SetIconFromStream(iconStream);
- desktopWindow.Title = Name;
- desktopWindow.DragDrop += f => fileDrop(new[] { f });
- break;
- }
+ desktopWindow.CursorState |= CursorState.Hidden;
+ desktopWindow.SetIconFromStream(iconStream);
+ desktopWindow.Title = Name;
+ desktopWindow.DragDrop += f => fileDrop(new[] { f });
}
private void fileDrop(string[] filePaths)
diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs
index 6ca7079654..d06c4b6746 100644
--- a/osu.Desktop/Program.cs
+++ b/osu.Desktop/Program.cs
@@ -22,9 +22,8 @@ namespace osu.Desktop
{
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
- bool useOsuTK = args.Contains("--tk");
- using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true, useOsuTK: useOsuTK))
+ using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
{
host.ExceptionThrown += handleException;
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs
index f15da29993..1248409b2a 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests
[BackgroundDependencyLoader]
private void load()
{
- LocalConfig.Set(OsuSetting.IncreaseFirstObjectVisibility, false);
+ LocalConfig.SetValue(OsuSetting.IncreaseFirstObjectVisibility, false);
}
[Test]
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
index e8bb57cdf3..48efd73222 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
@@ -202,7 +202,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public void TestHitLightingColour()
{
var fruitColour = SkinConfiguration.DefaultComboColours[1];
- AddStep("enable hit lighting", () => config.Set(OsuSetting.HitLighting, true));
+ AddStep("enable hit lighting", () => config.SetValue(OsuSetting.HitLighting, true));
AddStep("catch fruit", () => attemptCatch(new Fruit()));
AddAssert("correct hit lighting colour", () =>
catcher.ChildrenOfType().First()?.ObjectColour == fruitColour);
@@ -211,7 +211,7 @@ namespace osu.Game.Rulesets.Catch.Tests
[Test]
public void TestHitLightingDisabled()
{
- AddStep("disable hit lighting", () => config.Set(OsuSetting.HitLighting, false));
+ AddStep("disable hit lighting", () => config.SetValue(OsuSetting.HitLighting, false));
AddStep("catch fruit", () => attemptCatch(new Fruit()));
AddAssert("no hit lighting", () => !catcher.ChildrenOfType().Any());
}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs
index a5248c7712..399a46aa77 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
+ Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed");
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released");
}
@@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames");
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
+ Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
@@ -148,9 +148,9 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.IsTrue(generated.Frames.Count == frame_offset + 4, "Replay must have 4 generated frames");
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time");
- Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time");
+ Assert.AreEqual(3000, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time");
Assert.AreEqual(2000, generated.Frames[frame_offset + 1].Time, "Incorrect second note hit time");
- Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
+ Assert.AreEqual(4000, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time");
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key1), "Key1 has not been released");
@@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.Mania.Tests
// | | |
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
- beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY });
+ beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
var generated = new ManiaAutoGenerator(beatmap).Generate();
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs
index 596430f9e5..7ae69bf7d7 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs
@@ -5,11 +5,13 @@ using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Screens;
+using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Replays;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Objects;
@@ -345,6 +347,14 @@ namespace osu.Game.Rulesets.Mania.Tests
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
+
+ AddUntilStep("wait for head", () => currentPlayer.GameplayClockContainer.GameplayClock.CurrentTime >= time_head);
+ AddAssert("head is visible",
+ () => currentPlayer.ChildrenOfType()
+ .Single(note => note.HitObject == beatmap.HitObjects[0])
+ .Head
+ .Alpha == 1);
+
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
}
@@ -352,6 +362,8 @@ namespace osu.Game.Rulesets.Mania.Tests
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
+ public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
+
protected override bool PauseOnFocusLost => false;
public ScoreAccessibleReplayPlayer(Score score)
diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
index 756f2b7b2f..39d0f4bae4 100644
--- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
+++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs
@@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Mania.Configuration
{
base.InitialiseDefaults();
- Set(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5);
- Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
+ SetDefault(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5);
+ SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
index 75dcf0e55e..35ba2465fa 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.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.Objects.Drawables;
+
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
///
@@ -25,6 +27,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
LifetimeEnd = LifetimeStart + 30000;
}
+ protected override void UpdateHitStateTransforms(ArmedState state)
+ {
+ // suppress the base call explicitly.
+ // the hold note head should never change its visual state on its own due to the "freezing" mechanic
+ // (when hit, it remains visible in place at the judgement line; when dropped, it will scroll past the line).
+ // it will be hidden along with its parenting hold note when required.
+ }
+
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
public override void OnReleased(ManiaAction action)
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
index 3ebbe5af8e..7c51d58b74 100644
--- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Game.Replays;
using osu.Game.Rulesets.Mania.Beatmaps;
+using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Replays;
@@ -85,20 +86,28 @@ namespace osu.Game.Rulesets.Mania.Replays
{
var currentObject = Beatmap.HitObjects[i];
var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button
-
- double endTime = currentObject.GetEndTime();
-
- bool canDelayKeyUp = nextObjectInColumn == null ||
- nextObjectInColumn.StartTime > endTime + RELEASE_DELAY;
-
- double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInColumn.StartTime - endTime) * 0.9;
+ var releaseTime = calculateReleaseTime(currentObject, nextObjectInColumn);
yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column };
- yield return new ReleasePoint { Time = endTime + calculatedDelay, Column = currentObject.Column };
+ yield return new ReleasePoint { Time = releaseTime, Column = currentObject.Column };
}
}
+ private double calculateReleaseTime(HitObject currentObject, HitObject nextObject)
+ {
+ double endTime = currentObject.GetEndTime();
+
+ if (currentObject is HoldNote)
+ // hold note releases must be timed exactly.
+ return endTime;
+
+ bool canDelayKeyUpFully = nextObject == null ||
+ nextObject.StartTime > endTime + RELEASE_DELAY;
+
+ return endTime + (canDelayKeyUpFully ? RELEASE_DELAY : (nextObject.StartTime - endTime) * 0.9);
+ }
+
protected override HitObject GetNextObject(int currentIndex)
{
int desiredColumn = Beatmap.HitObjects[currentIndex].Column;
diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs
index 73aece1ed4..e4d466dca5 100644
--- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs
@@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
public class LegacyHitExplosion : LegacyManiaColumnElement, IHitExplosion
{
+ public const double FADE_IN_DURATION = 80;
+
private readonly IBindable direction = new Bindable();
private Drawable explosion;
@@ -72,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
(explosion as IFramedAnimation)?.GotoFrame(0);
- explosion?.FadeInFromZero(80)
+ explosion?.FadeInFromZero(FADE_IN_DURATION)
.Then().FadeOut(120);
}
}
diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs
index 78ccb83a8c..10319a7d4d 100644
--- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs
@@ -101,8 +101,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
if (action == column.Action.Value)
{
- upSprite.FadeTo(1);
- downSprite.FadeTo(0);
+ upSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(1);
+ downSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(0);
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs
index cbbbacfe19..24ccae895d 100644
--- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs
@@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
return animation == null ? null : new LegacyManiaJudgementPiece(result, animation);
}
- public override Sample GetSample(ISampleInfo sampleInfo)
+ public override ISample GetSample(ISampleInfo sampleInfo)
{
// layered hit sounds never play in mania
if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacySample && legacySample.IsLayered)
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
index a365ea10d4..c2119585ab 100644
--- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
@@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
- [TestCase(6.9311451172608853d, "diffcalc-test")]
- [TestCase(1.0736587013228804d, "zero-length-sliders")]
+ [TestCase(6.9311451172574934d, "diffcalc-test")]
+ [TestCase(1.0736586907780401d, "zero-length-sliders")]
public void Test(double expected, string name)
=> base.Test(expected, name);
- [TestCase(8.6228371119393064d, "diffcalc-test")]
- [TestCase(1.2864585434597433d, "zero-length-sliders")]
+ [TestCase(8.6228371119271454d, "diffcalc-test")]
+ [TestCase(1.2864585280364178d, "zero-length-sliders")]
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new OsuModDoubleTime());
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs
index e2d9f144c0..8fd13c7417 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs
@@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests
return null;
}
- public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
+ public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException();
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs
index e4158d8f07..4395ca6281 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[Test]
public void TestHitLightingDisabled()
{
- AddStep("hit lighting disabled", () => config.Set(OsuSetting.HitLighting, false));
+ AddStep("hit lighting disabled", () => config.SetValue(OsuSetting.HitLighting, false));
showResult(HitResult.Great);
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[Test]
public void TestHitLightingEnabled()
{
- AddStep("hit lighting enabled", () => config.Set(OsuSetting.HitLighting, true));
+ AddStep("hit lighting enabled", () => config.SetValue(OsuSetting.HitLighting, true));
showResult(HitResult.Great);
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
index 461779b185..e3ccf83715 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddSliderStep("circle size", 0f, 10f, 0f, val =>
{
- config.Set(OsuSetting.AutoCursorSize, true);
+ config.SetValue(OsuSetting.AutoCursorSize, true);
gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = val;
Scheduler.AddOnce(recreate);
});
@@ -64,21 +64,21 @@ namespace osu.Game.Rulesets.Osu.Tests
[TestCase(10, 1.5f)]
public void TestSizing(int circleSize, float userScale)
{
- AddStep($"set user scale to {userScale}", () => config.Set(OsuSetting.GameplayCursorSize, userScale));
+ AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale));
AddStep($"adjust cs to {circleSize}", () => gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize);
- AddStep("turn on autosizing", () => config.Set(OsuSetting.AutoCursorSize, true));
+ AddStep("turn on autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, true));
AddStep("load content", loadContent);
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize) * userScale);
- AddStep("set user scale to 1", () => config.Set(OsuSetting.GameplayCursorSize, 1f));
+ AddStep("set user scale to 1", () => config.SetValue(OsuSetting.GameplayCursorSize, 1f));
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize));
- AddStep("turn off autosizing", () => config.Set(OsuSetting.AutoCursorSize, false));
+ AddStep("turn off autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, false));
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == 1);
- AddStep($"set user scale to {userScale}", () => config.Set(OsuSetting.GameplayCursorSize, userScale));
+ AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale));
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == userScale);
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs
index 8dbb48c048..6c6f05c5c5 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs
@@ -42,10 +42,10 @@ namespace osu.Game.Rulesets.Osu.Tests
{
AddStep("enable user provider", () => testUserSkin.Enabled = true);
- AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true));
+ AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true));
checkNextHitObject("beatmap");
- AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false));
+ AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false));
checkNextHitObject("user");
AddStep("disable user provider", () => testUserSkin.Enabled = false);
@@ -57,20 +57,20 @@ namespace osu.Game.Rulesets.Osu.Tests
{
AddStep("enable user provider", () => testUserSkin.Enabled = true);
- AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true));
- AddStep("enable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, true));
+ AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true));
+ AddStep("enable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, true));
checkNextHitObject("beatmap");
- AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true));
- AddStep("disable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, false));
+ AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true));
+ AddStep("disable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, false));
checkNextHitObject("beatmap");
- AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false));
- AddStep("enable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, true));
+ AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false));
+ AddStep("enable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, true));
checkNextHitObject("user");
- AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false));
- AddStep("disable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, false));
+ AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false));
+ AddStep("disable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, false));
checkNextHitObject("user");
AddStep("disable user provider", () => testUserSkin.Enabled = false);
@@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
- public Sample GetSample(ISampleInfo sampleInfo) => null;
+ public ISample GetSample(ISampleInfo sampleInfo) => null;
public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => default;
public IBindable GetConfig(TLookup lookup) => null;
diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs
index e8272057f3..9589fd576f 100644
--- a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs
+++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs
@@ -17,10 +17,10 @@ namespace osu.Game.Rulesets.Osu.Configuration
protected override void InitialiseDefaults()
{
base.InitialiseDefaults();
- Set(OsuRulesetSetting.SnakingInSliders, true);
- Set(OsuRulesetSetting.SnakingOutSliders, true);
- Set(OsuRulesetSetting.ShowCursorTrail, true);
- Set(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None);
+ SetDefault(OsuRulesetSetting.SnakingInSliders, true);
+ SetDefault(OsuRulesetSetting.SnakingOutSliders, true);
+ SetDefault(OsuRulesetSetting.ShowCursorTrail, true);
+ SetDefault(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None);
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
index e9838de63d..1390675a1a 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
@@ -7,11 +7,13 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit;
using osuTK;
@@ -23,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
///
/// A visualisation of a single in a .
///
- public class PathControlPointPiece : BlueprintPiece
+ public class PathControlPointPiece : BlueprintPiece, IHasTooltip
{
public Action RequestSelection;
@@ -195,7 +197,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
markerRing.Alpha = IsSelected.Value ? 1 : 0;
- Color4 colour = ControlPoint.Type.Value != null ? colours.Red : colours.Yellow;
+ Color4 colour = getColourFromNodeType();
if (IsHovered || IsSelected.Value)
colour = colour.Lighten(1);
@@ -203,5 +205,28 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
marker.Colour = colour;
marker.Scale = new Vector2(slider.Scale);
}
+
+ private Color4 getColourFromNodeType()
+ {
+ if (!(ControlPoint.Type.Value is PathType pathType))
+ return colours.Yellow;
+
+ switch (pathType)
+ {
+ case PathType.Catmull:
+ return colours.Seafoam;
+
+ case PathType.Bezier:
+ return colours.Pink;
+
+ case PathType.PerfectCurve:
+ return colours.PurpleDark;
+
+ default:
+ return colours.Red;
+ }
+ }
+
+ public string TooltipText => ControlPoint.Type.Value.ToString() ?? string.Empty;
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
index 3d3dff653a..ba9bb3c485 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -28,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
protected SliderBodyPiece BodyPiece { get; private set; }
protected SliderCircleSelectionBlueprint HeadBlueprint { get; private set; }
protected SliderCircleSelectionBlueprint TailBlueprint { get; private set; }
+
+ [CanBeNull]
protected PathControlPointVisualiser ControlPointVisualiser { get; private set; }
private readonly DrawableSlider slider;
@@ -114,6 +117,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
// throw away frame buffers on deselection.
ControlPointVisualiser?.Expire();
+ ControlPointVisualiser = null;
+
BodyPiece.RecyclePath();
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 77094f928b..189003875d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -164,28 +164,29 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ApproachCircle.Expire(true);
}
+ protected override void UpdateStartTimeStateTransforms()
+ {
+ base.UpdateStartTimeStateTransforms();
+
+ ApproachCircle.FadeOut(50);
+ }
+
protected override void UpdateHitStateTransforms(ArmedState state)
{
Debug.Assert(HitObject.HitWindows != null);
+ // todo: temporary / arbitrary, used for lifetime optimisation.
+ this.Delay(800).FadeOut();
+
switch (state)
{
case ArmedState.Idle:
- this.Delay(HitObject.TimePreempt).FadeOut(500);
HitArea.HitAction = null;
break;
case ArmedState.Miss:
- ApproachCircle.FadeOut(50);
this.FadeOut(100);
break;
-
- case ArmedState.Hit:
- ApproachCircle.FadeOut(50);
-
- // todo: temporary / arbitrary
- this.Delay(800).FadeOut();
- break;
}
Expire();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index 3d614c2dbd..32a0a14dc0 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -39,6 +39,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private Bindable isSpinning;
private bool spinnerFrequencyModulate;
+ private const double fade_out_duration = 160;
+
public DrawableSpinner()
: this(null)
{
@@ -131,12 +133,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (tracking.NewValue)
{
if (!spinningSample.IsPlaying)
- spinningSample?.Play();
- spinningSample?.VolumeTo(1, 300);
+ spinningSample.Play();
+
+ spinningSample.VolumeTo(1, 300);
}
else
{
- spinningSample?.VolumeTo(0, 300).OnComplete(_ => spinningSample.Stop());
+ spinningSample.VolumeTo(0, fade_out_duration);
}
}
@@ -173,7 +176,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.UpdateHitStateTransforms(state);
- this.FadeOut(160).Expire();
+ this.FadeOut(fade_out_duration).OnComplete(_ =>
+ {
+ // looping sample should be stopped here as it is safer than running in the OnComplete
+ // of the volume transition above.
+ spinningSample.Stop();
+ });
+
+ Expire();
// skin change does a rewind of transforms, which will stop the spinning sound from playing if it's currently in playback.
isSpinning?.TriggerChange();
diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json
index 96e4bf1637..1a0bd66246 100644
--- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json
+++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json
@@ -1,5 +1,18 @@
{
"Mappings": [{
+ "StartTime": 114993,
+ "Objects": [{
+ "StartTime": 114993,
+ "EndTime": 114993,
+ "X": 493,
+ "Y": 92
+ }, {
+ "StartTime": 115290,
+ "EndTime": 115290,
+ "X": 451.659241,
+ "Y": 267.188
+ }]
+ }, {
"StartTime": 118858.0,
"Objects": [{
"StartTime": 118858.0,
diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu
index 8c3edc9571..dd35098502 100644
--- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu
+++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu
@@ -9,7 +9,9 @@ SliderMultiplier:1.87
SliderTickRate:1
[TimingPoints]
-49051,230.769230769231,4,2,1,15,1,0
+114000,346.820809248555,4,2,1,71,1,0
+118000,230.769230769231,4,2,1,15,1,0
[HitObjects]
+493,92,114993,2,0,P|472:181|442:308,1,180,12|0,0:0|0:0,0:0:0:0:
219,215,118858,2,0,P|224:170|244:-10,1,187,8|2,0:0|0:0,0:0:0:0:
diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs
index fcbe4c1b28..46aeadc59b 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs
@@ -74,10 +74,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
private void updateState(DrawableHitObject drawableObject, ArmedState state)
{
- using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
- {
+ using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
glow.FadeOut(400);
+ using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
+ {
switch (state)
{
case ArmedState.Hit:
diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs
index fecb5d4a74..61ea8b664d 100644
--- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs
@@ -2,8 +2,12 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
+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.Taiko.UI;
@@ -13,6 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
[TestFixture]
public class TestSceneHitExplosion : TaikoSkinnableTestScene
{
+ protected override double TimePerAction => 100;
+
[Test]
public void TestNormalHit()
{
@@ -21,11 +27,14 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss))));
}
- [Test]
- public void TestStrongHit([Values(false, true)] bool hitBoth)
+ [TestCase(HitResult.Great)]
+ [TestCase(HitResult.Ok)]
+ public void TestStrongHit(HitResult type)
{
- AddStep("Great", () => SetContents(() => getContentFor(createStrongHit(HitResult.Great, hitBoth))));
- AddStep("Good", () => SetContents(() => getContentFor(createStrongHit(HitResult.Ok, hitBoth))));
+ AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type))));
+ AddStep("visualise second hit",
+ () => this.ChildrenOfType()
+ .ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement()))));
}
private Drawable getContentFor(DrawableTestHit hit)
@@ -38,17 +47,17 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
// the hit needs to be added to hierarchy in order for nested objects to be created correctly.
// setting zero alpha is supposed to prevent the test from looking broken.
hit.With(h => h.Alpha = 0),
- new HitExplosion(hit, hit.Type)
+ new HitExplosion(hit.Type)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- }
+ }.With(explosion => explosion.Apply(hit))
}
};
}
private DrawableTestHit createHit(HitResult type) => new DrawableTestHit(new Hit { StartTime = Time.Current }, type);
- private DrawableTestHit createStrongHit(HitResult type, bool hitBoth) => new DrawableTestStrongHit(Time.Current, type, hitBoth);
+ private DrawableTestHit createStrongHit(HitResult type) => new DrawableTestStrongHit(Time.Current, type);
}
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs
index 7695ca067b..87c936d386 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs
@@ -11,7 +11,6 @@ using osu.Game.Audio;
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;
using osu.Game.Rulesets.Taiko.Objects;
@@ -108,12 +107,12 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great;
- Hit hit = new Hit();
+ Hit hit = new Hit { StartTime = DrawableRuleset.Playfield.Time.Current };
var h = new DrawableTestHit(hit, kiai: kiai) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) };
DrawableRuleset.Playfield.Add(h);
- ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
+ ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult });
}
private void addStrongHitJudgement(bool kiai)
@@ -122,6 +121,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Hit hit = new Hit
{
+ StartTime = DrawableRuleset.Playfield.Time.Current,
IsStrong = true,
Samples = createSamples(strong: true)
};
@@ -129,8 +129,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
DrawableRuleset.Playfield.Add(h);
- ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
- ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great });
+ ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult });
+ ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(hit.NestedHitObjects.Single(), new TaikoStrongJudgement()) { Type = HitResult.Great });
}
private void addMissJudgement()
diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs
index 651cdd6438..21bd35ad22 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs
@@ -1,22 +1,22 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Linq;
+using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Taiko.Objects.Drawables;
+using osu.Game.Rulesets.Taiko.UI;
namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
- public class LegacyHitExplosion : CompositeDrawable
+ public class LegacyHitExplosion : CompositeDrawable, IAnimatableHitExplosion
{
private readonly Drawable sprite;
- private readonly Drawable strongSprite;
- private DrawableStrongNestedHit nestedStrongHit;
- private bool switchedToStrongSprite;
+ [CanBeNull]
+ private readonly Drawable strongSprite;
///
/// Creates a new legacy hit explosion.
@@ -27,14 +27,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
///
/// The normal legacy explosion sprite.
/// The strong legacy explosion sprite.
- public LegacyHitExplosion(Drawable sprite, Drawable strongSprite = null)
+ public LegacyHitExplosion(Drawable sprite, [CanBeNull] Drawable strongSprite = null)
{
this.sprite = sprite;
this.strongSprite = strongSprite;
}
[BackgroundDependencyLoader]
- private void load(DrawableHitObject judgedObject)
+ private void load()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
@@ -56,45 +56,30 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
s.Origin = Anchor.Centre;
}));
}
-
- if (judgedObject is DrawableHit hit)
- nestedStrongHit = hit.NestedHitObjects.SingleOrDefault() as DrawableStrongNestedHit;
}
- protected override void LoadComplete()
+ public void Animate(DrawableHitObject drawableHitObject)
{
- base.LoadComplete();
-
const double animation_time = 120;
+ (sprite as IFramedAnimation)?.GotoFrame(0);
+ (strongSprite as IFramedAnimation)?.GotoFrame(0);
+
this.FadeInFromZero(animation_time).Then().FadeOut(animation_time * 1.5);
this.ScaleTo(0.6f)
.Then().ScaleTo(1.1f, animation_time * 0.8)
.Then().ScaleTo(0.9f, animation_time * 0.4)
.Then().ScaleTo(1f, animation_time * 0.2);
-
- Expire(true);
}
- protected override void Update()
+ public void AnimateSecondHit()
{
- base.Update();
+ if (strongSprite == null)
+ return;
- if (shouldSwitchToStrongSprite() && !switchedToStrongSprite)
- {
- sprite.FadeOut(50, Easing.OutQuint);
- strongSprite.FadeIn(50, Easing.OutQuint);
- switchedToStrongSprite = true;
- }
- }
-
- private bool shouldSwitchToStrongSprite()
- {
- if (nestedStrongHit == null || strongSprite == null)
- return false;
-
- return nestedStrongHit.IsHit;
+ sprite.FadeOut(50, Easing.OutQuint);
+ strongSprite.FadeIn(50, Easing.OutQuint);
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs
index d97da40ef2..3e506f69ce 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs
@@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}");
}
- public override Sample GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo));
+ public override ISample GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo));
public override IBindable GetConfig(TLookup lookup) => Source.GetConfig(lookup);
diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs
index 3bd20e4bb4..91e844187a 100644
--- a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -13,19 +14,23 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Taiko.UI
{
- internal class DefaultHitExplosion : CircularContainer
+ internal class DefaultHitExplosion : CircularContainer, IAnimatableHitExplosion
{
- private readonly DrawableHitObject judgedObject;
private readonly HitResult result;
- public DefaultHitExplosion(DrawableHitObject judgedObject, HitResult result)
+ [CanBeNull]
+ private Box body;
+
+ [Resolved]
+ private OsuColour colours { get; set; }
+
+ public DefaultHitExplosion(HitResult result)
{
- this.judgedObject = judgedObject;
this.result = result;
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load()
{
RelativeSizeAxes = Axes.Both;
@@ -40,26 +45,36 @@ namespace osu.Game.Rulesets.Taiko.UI
if (!result.IsHit())
return;
- bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
-
InternalChildren = new[]
{
- new Box
+ body = new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = isRim ? colours.BlueDarker : colours.PinkDarker,
}
};
+
+ updateColour();
}
- protected override void LoadComplete()
+ private void updateColour([CanBeNull] DrawableHitObject judgedObject = null)
{
- base.LoadComplete();
+ if (body == null)
+ return;
+
+ bool isRim = (judgedObject?.HitObject as Hit)?.Type == HitType.Rim;
+ body.Colour = isRim ? colours.BlueDarker : colours.PinkDarker;
+ }
+
+ public void Animate(DrawableHitObject drawableHitObject)
+ {
+ updateColour(drawableHitObject);
this.ScaleTo(3f, 1000, Easing.OutQuint);
this.FadeOut(500);
+ }
- Expire(true);
+ public void AnimateSecondHit()
+ {
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs
index d1fb3348b9..8f5e9e54ab 100644
--- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs
@@ -2,10 +2,12 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using JetBrains.Annotations;
using osuTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Pooling;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
@@ -16,31 +18,37 @@ namespace osu.Game.Rulesets.Taiko.UI
///
/// A circle explodes from the hit target to indicate a hitobject has been hit.
///
- internal class HitExplosion : CircularContainer
+ internal class HitExplosion : PoolableDrawable
{
public override bool RemoveWhenNotAlive => true;
-
- [Cached(typeof(DrawableHitObject))]
- public readonly DrawableHitObject JudgedObject;
+ public override bool RemoveCompletedTransforms => false;
private readonly HitResult result;
+ private double? secondHitTime;
+
+ [CanBeNull]
+ public DrawableHitObject JudgedObject;
+
private SkinnableDrawable skinnable;
- public override double LifetimeStart => skinnable.Drawable.LifetimeStart;
-
- public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd;
-
- public HitExplosion(DrawableHitObject judgedObject, HitResult result)
+ ///
+ /// This constructor only exists to meet the new() type constraint of .
+ ///
+ public HitExplosion()
+ : this(HitResult.Great)
+ {
+ }
+
+ public HitExplosion(HitResult result)
{
- JudgedObject = judgedObject;
this.result = result;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- RelativeSizeAxes = Axes.Both;
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE);
+ RelativeSizeAxes = Axes.Both;
RelativePositionAxes = Axes.Both;
}
@@ -48,7 +56,47 @@ namespace osu.Game.Rulesets.Taiko.UI
[BackgroundDependencyLoader]
private void load()
{
- Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(result)), _ => new DefaultHitExplosion(JudgedObject, result));
+ InternalChild = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(result)), _ => new DefaultHitExplosion(result));
+ skinnable.OnSkinChanged += runAnimation;
+ }
+
+ public void Apply([CanBeNull] DrawableHitObject drawableHitObject)
+ {
+ JudgedObject = drawableHitObject;
+ secondHitTime = null;
+ }
+
+ protected override void PrepareForUse()
+ {
+ base.PrepareForUse();
+ runAnimation();
+ }
+
+ private void runAnimation()
+ {
+ if (JudgedObject?.Result == null)
+ return;
+
+ double resultTime = JudgedObject.Result.TimeAbsolute;
+
+ LifetimeStart = resultTime;
+
+ ApplyTransformsAt(double.MinValue, true);
+ ClearTransforms(true);
+
+ using (BeginAbsoluteSequence(resultTime))
+ (skinnable.Drawable as IAnimatableHitExplosion)?.Animate(JudgedObject);
+
+ if (secondHitTime != null)
+ {
+ using (BeginAbsoluteSequence(secondHitTime.Value))
+ {
+ this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50);
+ (skinnable.Drawable as IAnimatableHitExplosion)?.AnimateSecondHit();
+ }
+ }
+
+ LifetimeEnd = skinnable.Drawable.LatestTransformEndTime;
}
private static TaikoSkinComponents getComponentName(HitResult result)
@@ -68,12 +116,10 @@ namespace osu.Game.Rulesets.Taiko.UI
throw new ArgumentOutOfRangeException(nameof(result), $"Invalid result type: {result}");
}
- ///
- /// Transforms this hit explosion to visualise a secondary hit.
- ///
- public void VisualiseSecondHit()
+ public void VisualiseSecondHit(JudgementResult judgementResult)
{
- this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50);
+ secondHitTime = judgementResult.TimeAbsolute;
+ runAnimation();
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs
new file mode 100644
index 0000000000..badf34554c
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs
@@ -0,0 +1,24 @@
+// 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.Graphics.Pooling;
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Taiko.UI
+{
+ ///
+ /// Pool for hit explosions of a specific type.
+ ///
+ internal class HitExplosionPool : DrawablePool
+ {
+ private readonly HitResult hitResult;
+
+ public HitExplosionPool(HitResult hitResult)
+ : base(15)
+ {
+ this.hitResult = hitResult;
+ }
+
+ protected override HitExplosion CreateNewDrawable() => new HitExplosion(hitResult);
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs
new file mode 100644
index 0000000000..cf0f5f9fb6
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs
@@ -0,0 +1,23 @@
+// 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.Drawables;
+
+namespace osu.Game.Rulesets.Taiko.UI
+{
+ ///
+ /// A skinnable element of a hit explosion that supports playing an animation from the current point in time.
+ ///
+ public interface IAnimatableHitExplosion
+ {
+ ///
+ /// Shows the hit explosion for the supplied .
+ ///
+ void Animate(DrawableHitObject drawableHitObject);
+
+ ///
+ /// Transforms the hit explosion to visualise a secondary hit.
+ ///
+ void AnimateSecondHit();
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index d2e7b604bb..46dafc3a30 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI
private SkinnableDrawable mascot;
private readonly IDictionary> judgementPools = new Dictionary>();
+ private readonly IDictionary explosionPools = new Dictionary();
private ProxyContainer topLevelHitContainer;
private Container rightArea;
@@ -166,10 +167,15 @@ namespace osu.Game.Rulesets.Taiko.UI
RegisterPool(100);
var hitWindows = new TaikoHitWindows();
+
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => hitWindows.IsHitResultAllowed(r)))
+ {
judgementPools.Add(result, new DrawablePool(15));
+ explosionPools.Add(result, new HitExplosionPool(result));
+ }
AddRangeInternal(judgementPools.Values);
+ AddRangeInternal(explosionPools.Values);
}
protected override void LoadComplete()
@@ -281,7 +287,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{
case TaikoStrongJudgement _:
if (result.IsHit)
- hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit();
+ hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(result);
break;
case TaikoDrumRollTickJudgement _:
@@ -315,7 +321,8 @@ namespace osu.Game.Rulesets.Taiko.UI
private void addExplosion(DrawableHitObject drawableObject, HitResult result, HitType type)
{
- hitExplosionContainer.Add(new HitExplosion(drawableObject, result));
+ hitExplosionContainer.Add(explosionPools[result]
+ .Get(explosion => explosion.Apply(drawableObject)));
if (drawableObject.HitObject.Kiai)
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
}
diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs
index 3ded3009bd..883791c35c 100644
--- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs
+++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs
@@ -121,7 +121,7 @@ namespace osu.Game.Tests.Gameplay
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException();
- public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
+ public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public IBindable GetConfig(TLookup lookup)
{
diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs
index 10a1a13ba0..cae5f20332 100644
--- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs
+++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Tests.Gameplay
public void TestRetrieveTopLevelSample()
{
ISkin skin = null;
- Sample channel = null;
+ ISample channel = null;
AddStep("create skin", () => skin = new TestSkin("test-sample", this));
AddStep("retrieve sample", () => channel = skin.GetSample(new SampleInfo("test-sample")));
@@ -48,7 +48,7 @@ namespace osu.Game.Tests.Gameplay
public void TestRetrieveSampleInSubFolder()
{
ISkin skin = null;
- Sample channel = null;
+ ISample channel = null;
AddStep("create skin", () => skin = new TestSkin("folder/test-sample", this));
AddStep("retrieve sample", () => channel = skin.GetSample(new SampleInfo("folder/test-sample")));
diff --git a/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs b/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs
index b90382488f..27cece42e8 100644
--- a/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs
+++ b/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs
@@ -88,7 +88,7 @@ namespace osu.Game.Tests.Input
=> AddStep($"make window {mode}", () => frameworkConfigManager.GetBindable(FrameworkSetting.WindowMode).Value = mode);
private void setGameSideModeTo(OsuConfineMouseMode mode)
- => AddStep($"set {mode} game-side", () => Game.LocalConfig.Set(OsuSetting.ConfineMouseMode, mode));
+ => AddStep($"set {mode} game-side", () => Game.LocalConfig.SetValue(OsuSetting.ConfineMouseMode, mode));
private void setLocalUserPlayingTo(bool playing)
=> AddStep($"local user {(playing ? "playing" : "not playing")}", () => Game.LocalUserPlaying.Value = playing);
diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs
index 045246e5ed..a763544c37 100644
--- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs
+++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs
@@ -47,7 +47,7 @@ namespace osu.Game.Tests.NonVisual
using (var host = new CustomTestHeadlessGameHost())
{
using (var storageConfig = new StorageConfigManager(host.InitialStorage))
- storageConfig.Set(StorageConfig.FullPath, customPath);
+ storageConfig.SetValue(StorageConfig.FullPath, customPath);
try
{
@@ -73,7 +73,7 @@ namespace osu.Game.Tests.NonVisual
using (var host = new CustomTestHeadlessGameHost())
{
using (var storageConfig = new StorageConfigManager(host.InitialStorage))
- storageConfig.Set(StorageConfig.FullPath, customPath);
+ storageConfig.SetValue(StorageConfig.FullPath, customPath);
try
{
diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs
index da004b9088..b08a228de3 100644
--- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs
+++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs
@@ -59,7 +59,7 @@ namespace osu.Game.Tests.NonVisual.Skinning
}
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
- public Sample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
+ public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
public IBindable GetConfig(TLookup lookup) => throw new NotSupportedException();
}
diff --git a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs
index 42948c3731..aa29d76843 100644
--- a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs
+++ b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs
@@ -23,8 +23,10 @@ namespace osu.Game.Tests.Online
{
case CommentVoteRequest cRequest:
cRequest.TriggerSuccess(new CommentBundle());
- break;
+ return true;
}
+
+ return false;
});
CommentVoteRequest request = null;
@@ -108,8 +110,10 @@ namespace osu.Game.Tests.Online
{
case LeaveChannelRequest cRequest:
cRequest.TriggerSuccess();
- break;
+ return true;
}
+
+ return false;
});
}
}
diff --git a/osu.Game.Tests/Resources/skin-with-space.ini b/osu.Game.Tests/Resources/skin-with-space.ini
new file mode 100644
index 0000000000..3e64257a3e
--- /dev/null
+++ b/osu.Game.Tests/Resources/skin-with-space.ini
@@ -0,0 +1,2 @@
+[General]
+Version: 2
\ No newline at end of file
diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
index aedf26ee75..dcb866c99f 100644
--- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
+++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs
@@ -91,6 +91,15 @@ namespace osu.Game.Tests.Skins
Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion);
}
+ [Test]
+ public void TestStripWhitespace()
+ {
+ var decoder = new LegacySkinDecoder();
+ using (var resStream = TestResources.OpenResource("skin-with-space.ini"))
+ using (var stream = new LineBufferedReader(resStream))
+ Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion);
+ }
+
[Test]
public void TestDecodeLatestVersion()
{
diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs
index 414f7d3f88..732a3f3f42 100644
--- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs
+++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs
@@ -219,7 +219,7 @@ namespace osu.Game.Tests.Skins
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => skin.GetTexture(componentName, wrapModeS, wrapModeT);
- public Sample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
+ public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
public IBindable GetConfig(TLookup lookup) => skin.GetConfig(lookup);
}
diff --git a/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs b/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs
index fba0d92d4b..dc5a4f4a3e 100644
--- a/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs
+++ b/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs
@@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Background
public void SetUp() => Schedule(() =>
{
// reset API response in statics to avoid test crosstalk.
- statics.Set(Static.SeasonalBackgrounds, null);
+ statics.SetValue(Static.SeasonalBackgrounds, null);
textureStore.PerformedLookups.Clear();
dummyAPI.SetState(APIState.Online);
@@ -135,18 +135,20 @@ namespace osu.Game.Tests.Visual.Background
dummyAPI.HandleRequest = request =>
{
if (dummyAPI.State.Value != APIState.Online || !(request is GetSeasonalBackgroundsRequest backgroundsRequest))
- return;
+ return false;
backgroundsRequest.TriggerSuccess(new APISeasonalBackgrounds
{
Backgrounds = seasonal_background_urls.Select(url => new APISeasonalBackground { Url = url }).ToList(),
EndDate = endDate
});
+
+ return true;
};
});
private void setSeasonalBackgroundMode(SeasonalBackgroundMode mode)
- => AddStep($"set seasonal mode to {mode}", () => config.Set(OsuSetting.SeasonalBackgroundMode, mode));
+ => AddStep($"set seasonal mode to {mode}", () => config.SetValue(OsuSetting.SeasonalBackgroundMode, mode));
private void createLoader()
=> AddStep("create loader", () =>
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs
new file mode 100644
index 0000000000..fd9c09fd5f
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs
@@ -0,0 +1,70 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Screens.Edit.Compose.Components;
+using osu.Game.Tests.Beatmaps;
+using osuTK;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Visual.Editing
+{
+ public class TestSceneBlueprintSelection : EditorTestScene
+ {
+ protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
+
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
+
+ private BlueprintContainer blueprintContainer
+ => Editor.ChildrenOfType().First();
+
+ [Test]
+ public void TestSelectedObjectHasPriorityWhenOverlapping()
+ {
+ var firstSlider = new Slider
+ {
+ Path = new SliderPath(new[]
+ {
+ new PathControlPoint(new Vector2()),
+ new PathControlPoint(new Vector2(150, -50)),
+ new PathControlPoint(new Vector2(300, 0))
+ }),
+ Position = new Vector2(0, 100)
+ };
+ var secondSlider = new Slider
+ {
+ Path = new SliderPath(new[]
+ {
+ new PathControlPoint(new Vector2()),
+ new PathControlPoint(new Vector2(-50, 50)),
+ new PathControlPoint(new Vector2(-100, 100))
+ }),
+ Position = new Vector2(200, 0)
+ };
+
+ AddStep("add overlapping sliders", () =>
+ {
+ EditorBeatmap.Add(firstSlider);
+ EditorBeatmap.Add(secondSlider);
+ });
+ AddStep("select first slider", () => EditorBeatmap.SelectedHitObjects.Add(firstSlider));
+
+ AddStep("move mouse to common point", () =>
+ {
+ var pos = blueprintContainer.ChildrenOfType().ElementAt(1).ScreenSpaceDrawQuad.Centre;
+ InputManager.MoveMouseTo(pos);
+ });
+ AddStep("right click", () => InputManager.Click(MouseButton.Right));
+
+ AddAssert("selection is unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == firstSlider);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs
new file mode 100644
index 0000000000..390198be04
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs
@@ -0,0 +1,81 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.Edit.Components;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Editing
+{
+ [TestFixture]
+ public class TestSceneEditorClock : EditorClockTestScene
+ {
+ public TestSceneEditorClock()
+ {
+ Add(new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new TimeInfoContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(200, 100)
+ },
+ new PlaybackControl
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(200, 100)
+ }
+ }
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
+ // ensure that music controller does not change this beatmap due to it
+ // completing naturally as part of the test.
+ Beatmap.Disabled = true;
+ }
+
+ [Test]
+ public void TestStopAtTrackEnd()
+ {
+ AddStep("reset clock", () => Clock.Seek(0));
+
+ AddStep("start clock", Clock.Start);
+ AddAssert("clock running", () => Clock.IsRunning);
+
+ AddStep("seek near end", () => Clock.Seek(Clock.TrackLength - 250));
+ AddUntilStep("clock stops", () => !Clock.IsRunning);
+
+ AddAssert("clock stopped at end", () => Clock.CurrentTime == Clock.TrackLength);
+
+ AddStep("start clock again", Clock.Start);
+ AddAssert("clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500);
+ }
+
+ [Test]
+ public void TestWrapWhenStoppedAtTrackEnd()
+ {
+ AddStep("reset clock", () => Clock.Seek(0));
+
+ AddStep("stop clock", Clock.Stop);
+ AddAssert("clock stopped", () => !Clock.IsRunning);
+
+ AddStep("seek exactly to end", () => Clock.Seek(Clock.TrackLength));
+ AddAssert("clock stopped at end", () => Clock.CurrentTime == Clock.TrackLength);
+
+ AddStep("start clock again", Clock.Start);
+ AddAssert("clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs
index d6db171cf0..1da6433707 100644
--- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs
+++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs
@@ -110,8 +110,6 @@ namespace osu.Game.Tests.Visual.Editing
[Resolved]
private EditorClock editorClock { get; set; }
- private bool started;
-
public StartStopButton()
{
BackgroundColour = Color4.SlateGray;
@@ -123,18 +121,17 @@ namespace osu.Game.Tests.Visual.Editing
private void onClick()
{
- if (started)
- {
+ if (editorClock.IsRunning)
editorClock.Stop();
- Text = "Start";
- }
else
- {
editorClock.Start();
- Text = "Stop";
- }
+ }
- started = !started;
+ protected override void Update()
+ {
+ base.Update();
+
+ Text = editorClock.IsRunning ? "Stop" : "Start";
}
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs
index 1c55595c97..5a1a9d3d87 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay
});
AddStep("show health", () => showHealth.Value = true);
- AddStep("enable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true));
+ AddStep("enable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
AddUntilStep("layer is visible", () => layer.IsPresent);
}
@@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestLayerDisabledViaConfig()
{
- AddStep("disable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false));
+ AddStep("disable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
AddUntilStep("layer is not visible", () => !layer.IsPresent);
}
@@ -81,19 +81,19 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
AddStep("don't show health", () => showHealth.Value = false);
- AddStep("disable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false));
+ AddStep("disable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
AddUntilStep("layer fade is invisible", () => !layer.IsPresent);
AddStep("don't show health", () => showHealth.Value = false);
- AddStep("enable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true));
+ AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
AddUntilStep("layer fade is invisible", () => !layer.IsPresent);
AddStep("show health", () => showHealth.Value = true);
- AddStep("disable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false));
+ AddStep("disable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
AddUntilStep("layer fade is invisible", () => !layer.IsPresent);
AddStep("show health", () => showHealth.Value = true);
- AddStep("enable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true));
+ AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
AddUntilStep("layer fade is visible", () => layer.IsPresent);
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
index f9914e0193..3cefb8623f 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
@@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode));
- AddStep("set hud to never show", () => config.Set(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
+ AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
@@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("stop trigering", () => InputManager.ReleaseKey(Key.ControlLeft));
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
- AddStep("set original config value", () => config.Set(OsuSetting.HUDVisibilityMode, originalConfigValue));
+ AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
}
[Test]
@@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("set keycounter visible false", () =>
{
- config.Set(OsuSetting.KeyOverlay, false);
+ config.SetValue(OsuSetting.KeyOverlay, false);
hudOverlay.KeyCounter.AlwaysVisible.Value = false;
});
@@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("hidetarget is visible", () => hideTarget.IsPresent);
AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent);
- AddStep("return value", () => config.Set(OsuSetting.KeyOverlay, keyCounterVisibleValue));
+ AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue));
}
private void createNew(Action action = null)
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
index 44142b69d7..7a6e2f54c2 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
@@ -298,7 +298,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException();
- public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
+ public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException();
}
@@ -309,7 +309,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException();
- public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
+ public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException();
}
@@ -321,7 +321,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException();
- public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
+ public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException();
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs
index d688e9cb21..d792405eeb 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs
@@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public Drawable GetDrawableComponent(ISkinComponent component) => source?.GetDrawableComponent(component);
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source?.GetTexture(componentName, wrapModeS, wrapModeT);
- public Sample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo);
+ public ISample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo);
public IBindable GetConfig(TLookup lookup) => source?.GetConfig(lookup);
public void TriggerSourceChanged()
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardSamplePlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardSamplePlayback.cs
index 1544f8fd35..a718a98aa6 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardSamplePlayback.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardSamplePlayback.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
- config.Set(OsuSetting.ShowStoryboard, true);
+ config.SetValue(OsuSetting.ShowStoryboard, true);
storyboard = new Storyboard();
var backgroundLayer = storyboard.GetLayer("Background");
diff --git a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs
index 96393cc4c3..bf5338d81a 100644
--- a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs
+++ b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
-using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
@@ -62,10 +61,6 @@ namespace osu.Game.Tests.Visual.Navigation
RecycleLocalStorage();
- // see MouseSettings
- var frameworkConfig = host.Dependencies.Get();
- frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity).Disabled = false;
-
CreateGame();
});
@@ -82,7 +77,7 @@ namespace osu.Game.Tests.Visual.Navigation
// todo: this can be removed once we can run audio tracks without a device present
// see https://github.com/ppy/osu/issues/1302
- Game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
+ Game.LocalConfig.SetValue(OsuSetting.IntroSequence, IntroSequence.Circles);
Add(Game);
}
@@ -136,7 +131,7 @@ namespace osu.Game.Tests.Visual.Navigation
base.LoadComplete();
API.Login("Rhythm Champion", "osu!");
- Dependencies.Get().Set(Static.MutedAudioNotificationShownOnce, true);
+ Dependencies.Get().SetValue(Static.MutedAudioNotificationShownOnce, true);
}
}
diff --git a/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs b/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs
index c0b77b580e..c1c968e862 100644
--- a/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs
@@ -15,8 +15,8 @@ namespace osu.Game.Tests.Visual.Navigation
using (var config = new OsuConfigManager(LocalStorage))
{
- config.Set(OsuSetting.Version, "2020.101.0");
- config.Set(OsuSetting.DisplayStarsMaximum, 10.0);
+ config.SetValue(OsuSetting.Version, "2020.101.0");
+ config.SetValue(OsuSetting.DisplayStarsMaximum, 10.0);
}
}
@@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Navigation
{
AddAssert("config has migrated value", () => Precision.AlmostEquals(Game.LocalConfig.Get(OsuSetting.DisplayStarsMaximum), 10.1));
- AddStep("set value again", () => Game.LocalConfig.Set(OsuSetting.DisplayStarsMaximum, 10));
+ AddStep("set value again", () => Game.LocalConfig.SetValue(OsuSetting.DisplayStarsMaximum, 10.0));
AddStep("force save config", () => Game.LocalConfig.Save());
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs
index 1349264bf9..156d6b744e 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs
@@ -30,13 +30,14 @@ namespace osu.Game.Tests.Visual.Online
((DummyAPIAccess)API).HandleRequest = req =>
{
- if (req is SearchBeatmapSetsRequest searchBeatmapSetsRequest)
+ if (!(req is SearchBeatmapSetsRequest searchBeatmapSetsRequest)) return false;
+
+ searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse
{
- searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse
- {
- BeatmapSets = setsForResponse,
- });
- }
+ BeatmapSets = setsForResponse,
+ });
+
+ return true;
};
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
index cd2c4e9346..8818ac75b1 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
@@ -63,13 +63,15 @@ namespace osu.Game.Tests.Visual.Online
Builds = builds.Values.ToList()
};
changelogRequest.TriggerSuccess(changelogResponse);
- break;
+ return true;
case GetChangelogBuildRequest buildRequest:
if (requestedBuild != null)
buildRequest.TriggerSuccess(requestedBuild);
- break;
+ return true;
}
+
+ return false;
};
Child = changelog = new TestChangelogOverlay();
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
index fca642ad6c..b13dd34ebc 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
@@ -11,6 +11,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
using osu.Game.Overlays;
using osu.Game.Overlays.Chat.Selection;
@@ -64,6 +66,24 @@ namespace osu.Game.Tests.Visual.Online
});
}
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("register request handling", () =>
+ {
+ ((DummyAPIAccess)API).HandleRequest = req =>
+ {
+ switch (req)
+ {
+ case JoinChannelRequest _:
+ return true;
+ }
+
+ return false;
+ };
+ });
+ }
+
[Test]
public void TestHideOverlay()
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
index c2a18330c9..cd22bb2513 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
@@ -85,9 +85,10 @@ namespace osu.Game.Tests.Visual.Online
dummyAPI.HandleRequest = request =>
{
if (!(request is GetCommentsRequest getCommentsRequest))
- return;
+ return false;
getCommentsRequest.TriggerSuccess(commentBundle);
+ return true;
};
});
diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs
index 37d51c16d2..6ebe8fcc07 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs
@@ -33,9 +33,10 @@ namespace osu.Game.Tests.Visual.Online
dummyAPI.HandleRequest = request =>
{
if (!(request is GetNewsRequest getNewsRequest))
- return;
+ return false;
getNewsRequest.TriggerSuccess(r);
+ return true;
};
});
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
index be8032cde8..61d49e4018 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
@@ -170,6 +170,17 @@ namespace osu.Game.Tests.Visual.Playlists
private void bindHandler(bool delayed = false, ScoreInfo userScore = null, bool failRequests = false) => ((DummyAPIAccess)API).HandleRequest = request =>
{
+ // pre-check for requests we should be handling (as they are scheduled below).
+ switch (request)
+ {
+ case ShowPlaylistUserScoreRequest _:
+ case IndexPlaylistScoresRequest _:
+ break;
+
+ default:
+ return false;
+ }
+
requestComplete = false;
double delay = delayed ? 3000 : 0;
@@ -196,6 +207,8 @@ namespace osu.Game.Tests.Visual.Playlists
break;
}
}, delay);
+
+ return true;
};
private void triggerSuccess(APIRequest req, T result)
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
index 2f558a6379..591095252f 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
@@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Ranking
}));
AddAssert("mapped by text not present", () =>
- this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Current.Value, "mapped", "by")));
+ this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text.ToString(), "mapped", "by")));
}
private void showPanel(ScoreInfo score) => Child = new ExpandedPanelMiddleContentContainer(score);
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs
new file mode 100644
index 0000000000..a7f6c8c0d3
--- /dev/null
+++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs
@@ -0,0 +1,73 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Input.Handlers.Tablet;
+using osu.Framework.Platform;
+using osu.Framework.Utils;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Settings.Sections.Input;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Settings
+{
+ [TestFixture]
+ public class TestSceneTabletSettings : OsuTestScene
+ {
+ [BackgroundDependencyLoader]
+ private void load(GameHost host)
+ {
+ var tabletHandler = new TestTabletHandler();
+
+ AddRange(new Drawable[]
+ {
+ new TabletSettings(tabletHandler)
+ {
+ RelativeSizeAxes = Axes.None,
+ Width = SettingsPanel.WIDTH,
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ }
+ });
+
+ AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Vector2(160, 100)));
+ AddStep("Test with square tablet", () => tabletHandler.SetTabletSize(new Vector2(300, 300)));
+ AddStep("Test with tall tablet", () => tabletHandler.SetTabletSize(new Vector2(100, 300)));
+ AddStep("Test with very tall tablet", () => tabletHandler.SetTabletSize(new Vector2(100, 700)));
+ AddStep("Test no tablet present", () => tabletHandler.SetTabletSize(Vector2.Zero));
+ }
+
+ public class TestTabletHandler : ITabletHandler
+ {
+ public Bindable AreaOffset { get; } = new Bindable();
+ public Bindable AreaSize { get; } = new Bindable();
+
+ public IBindable Tablet => tablet;
+
+ private readonly Bindable tablet = new Bindable();
+
+ public BindableBool Enabled { get; } = new BindableBool(true);
+
+ public void SetTabletSize(Vector2 size)
+ {
+ tablet.Value = size != Vector2.Zero ? new TabletInfo($"test tablet T-{RNG.Next(999):000}", size) : null;
+
+ AreaSize.Default = new Vector2(size.X, size.Y);
+
+ // if it's clear the user has not configured the area, take the full area from the tablet that was just found.
+ if (AreaSize.Value == Vector2.Zero)
+ AreaSize.SetDefault();
+
+ AreaOffset.Default = new Vector2(size.X / 2, size.Y / 2);
+
+ // likewise with the position, use the centre point if it has not been configured.
+ // it's safe to assume no user would set their centre point to 0,0 for now.
+ if (AreaOffset.Value == Vector2.Zero)
+ AreaOffset.SetDefault();
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
index 53a956c77c..5e2d5eba5d 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
@@ -32,8 +32,10 @@ namespace osu.Game.Tests.Visual.SongSelect
{
case GetUserRequest userRequest:
userRequest.TriggerSuccess(getUser(userRequest.Ruleset.ID));
- break;
+ return true;
}
+
+ return false;
};
});
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index 35c6d62cb7..5731b1ac2c 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -207,14 +207,14 @@ namespace osu.Game.Tests.Visual.SongSelect
addRulesetImportStep(0);
addRulesetImportStep(0);
- AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, false));
+ AddStep("change convert setting", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, false));
createSongSelect();
AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
- AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true));
+ AddStep("change convert setting", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true));
AddStep("return", () => songSelect.MakeCurrent());
AddUntilStep("wait for current", () => songSelect.IsCurrentScreen());
@@ -297,13 +297,13 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap);
- AddStep(@"Sort by Artist", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Artist));
- AddStep(@"Sort by Title", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Title));
- AddStep(@"Sort by Author", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Author));
- AddStep(@"Sort by DateAdded", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.DateAdded));
- AddStep(@"Sort by BPM", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.BPM));
- AddStep(@"Sort by Length", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Length));
- AddStep(@"Sort by Difficulty", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Difficulty));
+ AddStep(@"Sort by Artist", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Artist));
+ AddStep(@"Sort by Title", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Title));
+ AddStep(@"Sort by Author", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Author));
+ AddStep(@"Sort by DateAdded", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.DateAdded));
+ AddStep(@"Sort by BPM", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.BPM));
+ AddStep(@"Sort by Length", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Length));
+ AddStep(@"Sort by Difficulty", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Difficulty));
}
[Test]
@@ -470,7 +470,7 @@ namespace osu.Game.Tests.Visual.SongSelect
changeRuleset(0);
// used for filter check below
- AddStep("allow convert display", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true));
+ AddStep("allow convert display", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true));
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
@@ -648,7 +648,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
int changeCount = 0;
- AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, false));
+ AddStep("change convert setting", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, false));
AddStep("bind beatmap changed", () =>
{
Beatmap.ValueChanged += onChange;
@@ -686,7 +686,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("selection changed only fired twice", () => changeCount == 2);
AddStep("unbind beatmap changed", () => Beatmap.ValueChanged -= onChange);
- AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true));
+ AddStep("change convert setting", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true));
// ReSharper disable once AccessToModifiedClosure
void onChange(ValueChangedEvent valueChangedEvent) => changeCount++;
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs
index a9747e73f9..9602758ffc 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs
@@ -92,10 +92,10 @@ namespace osu.Game.Tests.Visual.UserInterface
[Test]
public void TestExplicitConfig()
{
- AddStep("configure explicit content to allowed", () => localConfig.Set(OsuSetting.ShowOnlineExplicitContent, true));
+ AddStep("configure explicit content to allowed", () => localConfig.SetValue(OsuSetting.ShowOnlineExplicitContent, true));
AddAssert("explicit control set to show", () => control.ExplicitContent.Value == SearchExplicit.Show);
- AddStep("configure explicit content to disallowed", () => localConfig.Set(OsuSetting.ShowOnlineExplicitContent, false));
+ AddStep("configure explicit content to disallowed", () => localConfig.SetValue(OsuSetting.ShowOnlineExplicitContent, false));
AddAssert("explicit control set to hide", () => control.ExplicitContent.Value == SearchExplicit.Hide);
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs
index 45720548c8..493e2f54e5 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs
@@ -44,22 +44,22 @@ namespace osu.Game.Tests.Visual.UserInterface
protected override void InitialiseDefaults()
{
- Set(TestConfigSetting.ToggleSettingNoKeybind, false);
- Set(TestConfigSetting.EnumSettingNoKeybind, EnumSetting.Setting1);
- Set(TestConfigSetting.ToggleSettingWithKeybind, false);
- Set(TestConfigSetting.EnumSettingWithKeybind, EnumSetting.Setting1);
+ SetDefault(TestConfigSetting.ToggleSettingNoKeybind, false);
+ SetDefault(TestConfigSetting.EnumSettingNoKeybind, EnumSetting.Setting1);
+ SetDefault(TestConfigSetting.ToggleSettingWithKeybind, false);
+ SetDefault(TestConfigSetting.EnumSettingWithKeybind, EnumSetting.Setting1);
base.InitialiseDefaults();
}
- public void ToggleSetting(TestConfigSetting setting) => Set(setting, !Get(setting));
+ public void ToggleSetting(TestConfigSetting setting) => SetValue(setting, !Get(setting));
public void IncrementEnumSetting(TestConfigSetting setting)
{
var nextValue = Get(setting) + 1;
if (nextValue > EnumSetting.Setting4)
nextValue = EnumSetting.Setting1;
- Set(setting, nextValue);
+ SetValue(setting, nextValue);
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
diff --git a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs
index 567d9f0d62..46c3b8bc3b 100644
--- a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs
+++ b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
storage.DeleteDirectory(string.Empty);
using (var storageConfig = new TournamentStorageManager(storage))
- storageConfig.Set(StorageConfig.CurrentTournament, custom_tournament);
+ storageConfig.SetValue(StorageConfig.CurrentTournament, custom_tournament);
try
{
diff --git a/osu.Game.Tournament/IO/TournamentStorage.cs b/osu.Game.Tournament/IO/TournamentStorage.cs
index 2ba1b6be8f..5d9fed6288 100644
--- a/osu.Game.Tournament/IO/TournamentStorage.cs
+++ b/osu.Game.Tournament/IO/TournamentStorage.cs
@@ -70,7 +70,7 @@ namespace osu.Game.Tournament.IO
moveFileIfExists("drawings.ini", destination);
ChangeTargetStorage(newStorage);
- storageConfig.Set(StorageConfig.CurrentTournament, default_tournament);
+ storageConfig.SetValue(StorageConfig.CurrentTournament, default_tournament);
storageConfig.Save();
}
diff --git a/osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs b/osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs
index d197c0f5d9..1a2f5a1ff4 100644
--- a/osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs
+++ b/osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs
@@ -12,8 +12,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
protected override void InitialiseDefaults()
{
- Set(DrawingsConfig.Groups, 8, 1, 8);
- Set(DrawingsConfig.TeamsPerGroup, 8, 1, 8);
+ SetDefault(DrawingsConfig.Groups, 8, 1, 8);
+ SetDefault(DrawingsConfig.TeamsPerGroup, 8, 1, 8);
}
public DrawingsConfigManager(Storage storage)
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index cb0b3a8d09..b291edd19d 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -17,12 +17,12 @@ namespace osu.Game.Beatmaps
public abstract class BeatmapConverter : IBeatmapConverter
where T : HitObject
{
- private event Action> ObjectConverted;
+ private event Action> objectConverted;
event Action> IBeatmapConverter.ObjectConverted
{
- add => ObjectConverted += value;
- remove => ObjectConverted -= value;
+ add => objectConverted += value;
+ remove => objectConverted -= value;
}
public IBeatmap Beatmap { get; }
@@ -92,10 +92,10 @@ namespace osu.Game.Beatmaps
var converted = ConvertHitObject(obj, beatmap, cancellationToken);
- if (ObjectConverted != null)
+ if (objectConverted != null)
{
converted = converted.ToList();
- ObjectConverted.Invoke(obj, converted);
+ objectConverted.Invoke(obj, converted);
}
foreach (var c in converted)
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 29b3f5d3a3..b4ea898b7d 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -113,8 +113,6 @@ namespace osu.Game.Beatmaps
{
var metadata = new BeatmapMetadata
{
- Artist = "artist",
- Title = "title",
Author = user,
};
@@ -128,7 +126,6 @@ namespace osu.Game.Beatmaps
BaseDifficulty = new BeatmapDifficulty(),
Ruleset = ruleset,
Metadata = metadata,
- Version = "difficulty"
}
}
};
diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
index 0bc5605051..73337ab6f5 100644
--- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Beatmaps.ControlPoints
///
public readonly BindableDouble SpeedMultiplierBindable = new BindableDouble(1)
{
- Precision = 0.1,
+ Precision = 0.01,
Default = 1,
MinValue = 0.1,
MaxValue = 10
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 99dffa7041..40bc75e847 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -67,16 +67,14 @@ namespace osu.Game.Beatmaps.Formats
protected override void ParseLine(Beatmap beatmap, Section section, string line)
{
- var strippedLine = StripComments(line);
-
switch (section)
{
case Section.General:
- handleGeneral(strippedLine);
+ handleGeneral(line);
return;
case Section.Editor:
- handleEditor(strippedLine);
+ handleEditor(line);
return;
case Section.Metadata:
@@ -84,19 +82,19 @@ namespace osu.Game.Beatmaps.Formats
return;
case Section.Difficulty:
- handleDifficulty(strippedLine);
+ handleDifficulty(line);
return;
case Section.Events:
- handleEvent(strippedLine);
+ handleEvent(line);
return;
case Section.TimingPoints:
- handleTimingPoint(strippedLine);
+ handleTimingPoint(line);
return;
case Section.HitObjects:
- handleHitObject(strippedLine);
+ handleHitObject(line);
return;
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index df940e8c8e..d06478b9de 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -471,9 +471,6 @@ namespace osu.Game.Beatmaps.Formats
private string toLegacyCustomSampleBank(HitSampleInfo hitSampleInfo)
{
- if (hitSampleInfo == null)
- return "0";
-
if (hitSampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy)
return legacy.CustomSampleBank.ToString(CultureInfo.InvariantCulture);
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 2fb24c24e0..10a716963e 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -36,6 +36,8 @@ namespace osu.Game.Beatmaps.Formats
if (ShouldSkipLine(line))
continue;
+ line = StripComments(line).TrimEnd();
+
if (line.StartsWith('[') && line.EndsWith(']'))
{
if (!Enum.TryParse(line[1..^1], out section))
@@ -71,8 +73,6 @@ namespace osu.Game.Beatmaps.Formats
protected virtual void ParseLine(T output, Section section, string line)
{
- line = StripComments(line);
-
switch (section)
{
case Section.Colours:
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index b9bf6823b5..6301c42deb 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -45,8 +45,6 @@ namespace osu.Game.Beatmaps.Formats
protected override void ParseLine(Storyboard storyboard, Section section, string line)
{
- line = StripComments(line);
-
switch (section)
{
case Section.General:
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index d0fa45bb7a..387cfbb193 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -24,126 +24,126 @@ namespace osu.Game.Configuration
protected override void InitialiseDefaults()
{
// UI/selection defaults
- Set(OsuSetting.Ruleset, 0, 0, int.MaxValue);
- Set(OsuSetting.Skin, 0, -1, int.MaxValue);
+ SetDefault(OsuSetting.Ruleset, 0, 0, int.MaxValue);
+ SetDefault(OsuSetting.Skin, 0, -1, int.MaxValue);
- Set(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details);
- Set(OsuSetting.BeatmapDetailModsFilter, false);
+ SetDefault(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details);
+ SetDefault(OsuSetting.BeatmapDetailModsFilter, false);
- Set(OsuSetting.ShowConvertedBeatmaps, true);
- Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
- Set(OsuSetting.DisplayStarsMaximum, 10.1, 0, 10.1, 0.1);
+ SetDefault(OsuSetting.ShowConvertedBeatmaps, true);
+ SetDefault(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
+ SetDefault(OsuSetting.DisplayStarsMaximum, 10.1, 0, 10.1, 0.1);
- Set(OsuSetting.SongSelectGroupingMode, GroupMode.All);
- Set(OsuSetting.SongSelectSortingMode, SortMode.Title);
+ SetDefault(OsuSetting.SongSelectGroupingMode, GroupMode.All);
+ SetDefault(OsuSetting.SongSelectSortingMode, SortMode.Title);
- Set(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation);
+ SetDefault(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation);
- Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f);
+ SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f);
// Online settings
- Set(OsuSetting.Username, string.Empty);
- Set(OsuSetting.Token, string.Empty);
+ SetDefault(OsuSetting.Username, string.Empty);
+ SetDefault(OsuSetting.Token, string.Empty);
- Set(OsuSetting.AutomaticallyDownloadWhenSpectating, false);
+ SetDefault(OsuSetting.AutomaticallyDownloadWhenSpectating, false);
- Set(OsuSetting.SavePassword, false).ValueChanged += enabled =>
+ SetDefault(OsuSetting.SavePassword, false).ValueChanged += enabled =>
{
- if (enabled.NewValue) Set(OsuSetting.SaveUsername, true);
+ if (enabled.NewValue) SetValue(OsuSetting.SaveUsername, true);
};
- Set(OsuSetting.SaveUsername, true).ValueChanged += enabled =>
+ SetDefault(OsuSetting.SaveUsername, true).ValueChanged += enabled =>
{
- if (!enabled.NewValue) Set(OsuSetting.SavePassword, false);
+ if (!enabled.NewValue) SetValue(OsuSetting.SavePassword, false);
};
- Set(OsuSetting.ExternalLinkWarning, true);
- Set(OsuSetting.PreferNoVideo, false);
+ SetDefault(OsuSetting.ExternalLinkWarning, true);
+ SetDefault(OsuSetting.PreferNoVideo, false);
- Set(OsuSetting.ShowOnlineExplicitContent, false);
+ SetDefault(OsuSetting.ShowOnlineExplicitContent, false);
// Audio
- Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01);
+ SetDefault(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01);
- Set(OsuSetting.MenuVoice, true);
- Set(OsuSetting.MenuMusic, true);
+ SetDefault(OsuSetting.MenuVoice, true);
+ SetDefault(OsuSetting.MenuMusic, true);
- Set(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1);
+ SetDefault(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1);
// Input
- Set(OsuSetting.MenuCursorSize, 1.0f, 0.5f, 2f, 0.01f);
- Set(OsuSetting.GameplayCursorSize, 1.0f, 0.1f, 2f, 0.01f);
- Set(OsuSetting.AutoCursorSize, false);
+ SetDefault(OsuSetting.MenuCursorSize, 1.0f, 0.5f, 2f, 0.01f);
+ SetDefault(OsuSetting.GameplayCursorSize, 1.0f, 0.1f, 2f, 0.01f);
+ SetDefault(OsuSetting.AutoCursorSize, false);
- Set(OsuSetting.MouseDisableButtons, false);
- Set(OsuSetting.MouseDisableWheel, false);
- Set(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay);
+ SetDefault(OsuSetting.MouseDisableButtons, false);
+ SetDefault(OsuSetting.MouseDisableWheel, false);
+ SetDefault(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay);
// Graphics
- Set(OsuSetting.ShowFpsDisplay, false);
+ SetDefault(OsuSetting.ShowFpsDisplay, false);
- Set(OsuSetting.ShowStoryboard, true);
- Set(OsuSetting.BeatmapSkins, true);
- Set(OsuSetting.BeatmapColours, true);
- Set(OsuSetting.BeatmapHitsounds, true);
+ SetDefault(OsuSetting.ShowStoryboard, true);
+ SetDefault(OsuSetting.BeatmapSkins, true);
+ SetDefault(OsuSetting.BeatmapColours, true);
+ SetDefault(OsuSetting.BeatmapHitsounds, true);
- Set(OsuSetting.CursorRotation, true);
+ SetDefault(OsuSetting.CursorRotation, true);
- Set(OsuSetting.MenuParallax, true);
+ SetDefault(OsuSetting.MenuParallax, true);
// Gameplay
- Set(OsuSetting.DimLevel, 0.8, 0, 1, 0.01);
- Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
- Set(OsuSetting.LightenDuringBreaks, true);
+ SetDefault(OsuSetting.DimLevel, 0.8, 0, 1, 0.01);
+ SetDefault(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
+ SetDefault(OsuSetting.LightenDuringBreaks, true);
- Set(OsuSetting.HitLighting, true);
+ SetDefault(OsuSetting.HitLighting, true);
- Set(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always);
- Set(OsuSetting.ShowProgressGraph, true);
- Set(OsuSetting.ShowHealthDisplayWhenCantFail, true);
- Set(OsuSetting.FadePlayfieldWhenHealthLow, true);
- Set(OsuSetting.KeyOverlay, false);
- Set(OsuSetting.PositionalHitSounds, true);
- Set(OsuSetting.AlwaysPlayFirstComboBreak, true);
- Set(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth);
+ SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always);
+ SetDefault(OsuSetting.ShowProgressGraph, true);
+ SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true);
+ SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true);
+ SetDefault(OsuSetting.KeyOverlay, false);
+ SetDefault(OsuSetting.PositionalHitSounds, true);
+ SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true);
+ SetDefault(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth);
- Set(OsuSetting.FloatingComments, false);
+ SetDefault(OsuSetting.FloatingComments, false);
- Set(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised);
+ SetDefault(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised);
- Set(OsuSetting.IncreaseFirstObjectVisibility, true);
- Set(OsuSetting.GameplayDisableWinKey, true);
+ SetDefault(OsuSetting.IncreaseFirstObjectVisibility, true);
+ SetDefault(OsuSetting.GameplayDisableWinKey, true);
// Update
- Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
+ SetDefault(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
- Set(OsuSetting.Version, string.Empty);
+ SetDefault(OsuSetting.Version, string.Empty);
- Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg);
- Set(OsuSetting.ScreenshotCaptureMenuCursor, false);
+ SetDefault(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg);
+ SetDefault(OsuSetting.ScreenshotCaptureMenuCursor, false);
- Set(OsuSetting.SongSelectRightMouseScroll, false);
+ SetDefault(OsuSetting.SongSelectRightMouseScroll, false);
- Set(OsuSetting.Scaling, ScalingMode.Off);
+ SetDefault(OsuSetting.Scaling, ScalingMode.Off);
- Set(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f);
- Set(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f);
+ SetDefault(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f);
+ SetDefault(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f);
- Set(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f);
- Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f);
+ SetDefault(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f);
+ SetDefault(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f);
- Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
+ SetDefault(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
- Set(OsuSetting.UIHoldActivationDelay, 200f, 0f, 500f, 50f);
+ SetDefault(OsuSetting.UIHoldActivationDelay, 200f, 0f, 500f, 50f);
- Set(OsuSetting.IntroSequence, IntroSequence.Triangles);
+ SetDefault(OsuSetting.IntroSequence, IntroSequence.Triangles);
- Set(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin);
- Set(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes);
+ SetDefault(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin);
+ SetDefault(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes);
- Set(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full);
+ SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full);
- Set(OsuSetting.EditorWaveformOpacity, 1f);
+ SetDefault(OsuSetting.EditorWaveformOpacity, 1f);
}
public OsuConfigManager(Storage storage)
diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs
index fd401119ff..36eb6964dd 100644
--- a/osu.Game/Configuration/SessionStatics.cs
+++ b/osu.Game/Configuration/SessionStatics.cs
@@ -14,10 +14,10 @@ namespace osu.Game.Configuration
{
protected override void InitialiseDefaults()
{
- Set(Static.LoginOverlayDisplayed, false);
- Set(Static.MutedAudioNotificationShownOnce, false);
- Set(Static.LastHoverSoundPlaybackTime, (double?)null);
- Set(Static.SeasonalBackgrounds, null);
+ SetDefault(Static.LoginOverlayDisplayed, false);
+ SetDefault(Static.MutedAudioNotificationShownOnce, false);
+ SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null);
+ SetDefault(Static.SeasonalBackgrounds, null);
}
}
diff --git a/osu.Game/Configuration/StorageConfigManager.cs b/osu.Game/Configuration/StorageConfigManager.cs
index 929f8f22ad..90ea42b638 100644
--- a/osu.Game/Configuration/StorageConfigManager.cs
+++ b/osu.Game/Configuration/StorageConfigManager.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Configuration
{
base.InitialiseDefaults();
- Set(StorageConfig.FullPath, string.Empty);
+ SetDefault(StorageConfig.FullPath, string.Empty);
}
}
diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs
index 4aeda74be8..266eb11319 100644
--- a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs
+++ b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs
@@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Graphics.UserInterfaceV2
@@ -53,6 +54,14 @@ namespace osu.Game.Graphics.UserInterfaceV2
CornerRadius = CORNER_RADIUS,
};
+ public override bool AcceptsFocus => true;
+
+ protected override void OnFocus(FocusEvent e)
+ {
+ base.OnFocus(e);
+ GetContainingInputManager().ChangeFocus(Component);
+ }
+
protected override OsuTextBox CreateComponent() => CreateTextBox().With(t =>
{
t.OnCommit += (sender, newText) => OnCommit?.Invoke(sender, newText);
diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs
index 8097f61ea4..7df5d820ee 100644
--- a/osu.Game/IO/OsuStorage.cs
+++ b/osu.Game/IO/OsuStorage.cs
@@ -58,7 +58,7 @@ namespace osu.Game.IO
///
public void ResetCustomStoragePath()
{
- storageConfig.Set(StorageConfig.FullPath, string.Empty);
+ storageConfig.SetValue(StorageConfig.FullPath, string.Empty);
storageConfig.Save();
ChangeTargetStorage(defaultStorage);
@@ -103,7 +103,7 @@ namespace osu.Game.IO
public override void Migrate(Storage newStorage)
{
base.Migrate(newStorage);
- storageConfig.Set(StorageConfig.FullPath, newStorage.GetFullPath("."));
+ storageConfig.SetValue(StorageConfig.FullPath, newStorage.GetFullPath("."));
storageConfig.Save();
}
}
diff --git a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs
deleted file mode 100644
index 46447b607b..0000000000
--- a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs
+++ /dev/null
@@ -1,34 +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;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using osuTK;
-
-namespace osu.Game.IO.Serialization.Converters
-{
- ///
- /// A type of that serializes only the X and Y coordinates of a .
- ///
- public class Vector2Converter : JsonConverter
- {
- public override Vector2 ReadJson(JsonReader reader, Type objectType, Vector2 existingValue, bool hasExistingValue, JsonSerializer serializer)
- {
- var obj = JObject.Load(reader);
- return new Vector2((float)obj["x"], (float)obj["y"]);
- }
-
- public override void WriteJson(JsonWriter writer, Vector2 value, JsonSerializer serializer)
- {
- writer.WriteStartObject();
-
- writer.WritePropertyName("x");
- writer.WriteValue(value.X);
- writer.WritePropertyName("y");
- writer.WriteValue(value.Y);
-
- writer.WriteEndObject();
- }
- }
-}
diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs
index ac95d47c4b..ba188963ea 100644
--- a/osu.Game/IO/Serialization/IJsonSerializable.cs
+++ b/osu.Game/IO/Serialization/IJsonSerializable.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.Collections.Generic;
using Newtonsoft.Json;
-using osu.Game.IO.Serialization.Converters;
+using osu.Framework.IO.Serialization;
namespace osu.Game.IO.Serialization
{
@@ -28,7 +29,7 @@ namespace osu.Game.IO.Serialization
Formatting = Formatting.Indented,
ObjectCreationHandling = ObjectCreationHandling.Replace,
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
- Converters = new JsonConverter[] { new Vector2Converter() },
+ Converters = new List { new Vector2Converter() },
ContractResolver = new KeyContractResolver()
};
}
diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs
index ede64c0340..944525c119 100644
--- a/osu.Game/Online/API/APIAccess.cs
+++ b/osu.Game/Online/API/APIAccess.cs
@@ -89,7 +89,7 @@ namespace osu.Game.Online.API
thread.Start();
}
- private void onTokenChanged(ValueChangedEvent e) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty);
+ private void onTokenChanged(ValueChangedEvent e) => config.SetValue(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty);
internal new void Schedule(Action action) => base.Schedule(action);
@@ -134,7 +134,7 @@ namespace osu.Game.Online.API
state.Value = APIState.Connecting;
// save the username at this point, if the user requested for it to be.
- config.Set(OsuSetting.Username, config.Get(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty);
+ config.SetValue(OsuSetting.Username, config.Get(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty);
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(ProvidedUsername, password))
{
diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs
index a7174324d8..1a6868cfa4 100644
--- a/osu.Game/Online/API/APIRequest.cs
+++ b/osu.Game/Online/API/APIRequest.cs
@@ -131,8 +131,11 @@ namespace osu.Game.Online.API
{
}
+ private bool succeeded;
+
internal virtual void TriggerSuccess()
{
+ succeeded = true;
Success?.Invoke();
}
@@ -145,10 +148,7 @@ namespace osu.Game.Online.API
public void Fail(Exception e)
{
- if (WebRequest?.Completed == true)
- return;
-
- if (cancelled)
+ if (succeeded || cancelled)
return;
cancelled = true;
@@ -181,9 +181,13 @@ namespace osu.Game.Online.API
/// Whether we are in a failed or cancelled state.
private bool checkAndScheduleFailure()
{
- if (API == null || pendingFailure == null) return cancelled;
+ if (pendingFailure == null) return cancelled;
+
+ if (API == null)
+ pendingFailure();
+ else
+ API.Schedule(pendingFailure);
- API.Schedule(pendingFailure);
pendingFailure = null;
return true;
}
diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs
index 943b52db88..52f2365165 100644
--- a/osu.Game/Online/API/DummyAPIAccess.cs
+++ b/osu.Game/Online/API/DummyAPIAccess.cs
@@ -34,8 +34,9 @@ namespace osu.Game.Online.API
///
/// Provide handling logic for an arbitrary API request.
+ /// Should return true is a request was handled. If null or false return, the request will be failed with a .
///
- public Action HandleRequest;
+ public Func HandleRequest;
private readonly Bindable state = new Bindable(APIState.Online);
@@ -55,7 +56,12 @@ namespace osu.Game.Online.API
public virtual void Queue(APIRequest request)
{
- HandleRequest?.Invoke(request);
+ if (HandleRequest?.Invoke(request) != true)
+ {
+ // this will fail due to not receiving an APIAccess, and trigger a failure on the request.
+ // this is intended - any request in testing that needs non-failures should use HandleRequest.
+ request.Perform(this);
+ }
}
public void Perform(APIRequest request) => HandleRequest?.Invoke(request);
diff --git a/osu.Game/Online/Rooms/APIScoreToken.cs b/osu.Game/Online/Rooms/APIScoreToken.cs
index f652c1720d..6b559876de 100644
--- a/osu.Game/Online/Rooms/APIScoreToken.cs
+++ b/osu.Game/Online/Rooms/APIScoreToken.cs
@@ -8,6 +8,6 @@ namespace osu.Game.Online.Rooms
public class APIScoreToken
{
[JsonProperty("id")]
- public int ID { get; set; }
+ public long ID { get; set; }
}
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index f211723c59..53dc900254 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -758,9 +758,15 @@ namespace osu.Game
{
otherOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
- // show above others if not visible at all, else leave at current depth.
- if (!overlay.IsPresent)
+ // Partially visible so leave it at the current depth.
+ if (overlay.IsPresent)
+ return;
+
+ // Show above all other overlays.
+ if (overlay.IsLoaded)
overlayContent.ChangeChildDepth(overlay, (float)-Clock.CurrentTime);
+ else
+ overlay.Depth = (float)-Clock.CurrentTime;
}
private void forwardLoggedErrorsToNotifications()
@@ -880,13 +886,13 @@ namespace osu.Game
switch (action)
{
case GlobalAction.ResetInputSettings:
- frameworkConfig.GetBindable(FrameworkSetting.IgnoredInputHandlers).SetDefault();
- frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity).SetDefault();
+ Host.ResetInputHandlers();
frameworkConfig.GetBindable(FrameworkSetting.ConfineMouseMode).SetDefault();
return true;
case GlobalAction.ToggleGameplayMouseButtons:
- LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get(OsuSetting.MouseDisableButtons));
+ var mouseDisableButtons = LocalConfig.GetBindable(OsuSetting.MouseDisableButtons);
+ mouseDisableButtons.Value = !mouseDisableButtons.Value;
return true;
case GlobalAction.RandomSkin:
diff --git a/osu.Game/Overlays/Comments/DeletedCommentsCounter.cs b/osu.Game/Overlays/Comments/DeletedCommentsCounter.cs
index 56588ef0a8..8c40d79f7a 100644
--- a/osu.Game/Overlays/Comments/DeletedCommentsCounter.cs
+++ b/osu.Game/Overlays/Comments/DeletedCommentsCounter.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Comments
{
new SpriteIcon
{
- Icon = FontAwesome.Solid.Trash,
+ Icon = FontAwesome.Regular.TrashAlt,
Size = new Vector2(14),
},
countText = new OsuSpriteText
diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs
index 2925107766..fe61e532e1 100644
--- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs
+++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq;
+using Humanizer;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
@@ -113,7 +114,12 @@ namespace osu.Game.Overlays.Profile.Header
}
topLinkContainer.AddText("Contributed ");
- topLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"{api.WebsiteRootUrl}/users/{user.Id}/posts", creationParameters: embolden);
+ topLinkContainer.AddLink("forum post".ToQuantity(user.PostCount, "#,##0"), $"{api.WebsiteRootUrl}/users/{user.Id}/posts", creationParameters: embolden);
+
+ addSpacer(topLinkContainer);
+
+ topLinkContainer.AddText("Posted ");
+ topLinkContainer.AddLink("comment".ToQuantity(user.CommentsCount, "#,##0"), $"{api.WebsiteRootUrl}/comments?user_id={user.Id}", creationParameters: embolden);
string websiteWithoutProtocol = user.Website;
@@ -138,7 +144,6 @@ namespace osu.Game.Overlays.Profile.Header
if (!string.IsNullOrEmpty(user.Twitter))
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Discord, user.Discord);
- anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat");
anyInfoAdded |= tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtocol, user.Website);
// If no information was added to the bottomLinkContainer, hide it to avoid unwanted padding
diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
index d4d0976724..cdb24b784c 100644
--- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
+++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
@@ -23,51 +23,24 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
{
this.user.BindTo(user);
CountSection total;
- CountSection avaliable;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = 3;
- Children = new Drawable[]
- {
- new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Horizontal,
- Spacing = new Vector2(5, 0),
- Children = new[]
- {
- total = new CountTotal(),
- avaliable = new CountAvailable()
- }
- }
- };
- this.user.ValueChanged += u =>
- {
- total.Count = u.NewValue?.Kudosu.Total ?? 0;
- avaliable.Count = u.NewValue?.Kudosu.Available ?? 0;
- };
+ Child = total = new CountTotal();
+
+ this.user.ValueChanged += u => total.Count = u.NewValue?.Kudosu.Total ?? 0;
}
protected override bool OnClick(ClickEvent e) => true;
- private class CountAvailable : CountSection
- {
- public CountAvailable()
- : base("Kudosu Avaliable")
- {
- DescriptionText.Text = "Kudosu can be traded for kudosu stars, which will help your beatmap get more attention. This is the number of kudosu you haven't traded in yet.";
- }
- }
-
private class CountTotal : CountSection
{
public CountTotal()
: base("Total Kudosu Earned")
{
DescriptionText.AddText("Based on how much of a contribution the user has made to beatmap moderation. See ");
- DescriptionText.AddLink("this link", "https://osu.ppy.sh/wiki/Kudosu");
+ DescriptionText.AddLink("this page", "https://osu.ppy.sh/wiki/Kudosu");
DescriptionText.AddText(" for more information.");
}
}
@@ -80,13 +53,12 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
public new int Count
{
- set => valueText.Text = value.ToString();
+ set => valueText.Text = value.ToString("N0");
}
public CountSection(string header)
{
RelativeSizeAxes = Axes.X;
- Width = 0.5f;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Top = 10, Bottom = 20 };
Child = new FillFlowContainer
@@ -131,7 +103,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
private void load(OverlayColourProvider colourProvider)
{
lineBackground.Colour = colourProvider.Highlight1;
- DescriptionText.Colour = colourProvider.Foreground1;
}
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs
similarity index 70%
rename from osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
rename to osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs
index db6f24a954..79c73863cf 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs
@@ -5,17 +5,17 @@ using osu.Framework.Graphics;
namespace osu.Game.Overlays.Settings.Sections.Input
{
- public class KeyboardSettings : SettingsSubsection
+ public class BindingSettings : SettingsSubsection
{
- protected override string Header => "Keyboard";
+ protected override string Header => "Shortcut and gameplay bindings";
- public KeyboardSettings(KeyBindingPanel keyConfig)
+ public BindingSettings(KeyBindingPanel keyConfig)
{
Children = new Drawable[]
{
new SettingsButton
{
- Text = "Key configuration",
+ Text = "Configure",
TooltipText = "change global shortcut keys and gameplay bindings",
Action = keyConfig.ToggleVisibility
},
diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
index 3a78cff890..fb908a7669 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
@@ -1,11 +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.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
+using osu.Framework.Input.Handlers.Mouse;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
@@ -14,46 +14,45 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
public class MouseSettings : SettingsSubsection
{
+ private readonly MouseHandler mouseHandler;
+
protected override string Header => "Mouse";
- private readonly BindableBool rawInputToggle = new BindableBool();
-
- private Bindable configSensitivity;
+ private Bindable handlerSensitivity;
private Bindable localSensitivity;
- private Bindable ignoredInputHandlers;
-
private Bindable windowMode;
private SettingsEnumDropdown confineMouseModeSetting;
+ private Bindable relativeMode;
+
+ public MouseSettings(MouseHandler mouseHandler)
+ {
+ this.mouseHandler = mouseHandler;
+ }
[BackgroundDependencyLoader]
private void load(OsuConfigManager osuConfig, FrameworkConfigManager config)
{
// use local bindable to avoid changing enabled state of game host's bindable.
- configSensitivity = config.GetBindable(FrameworkSetting.CursorSensitivity);
- localSensitivity = configSensitivity.GetUnboundCopy();
+ handlerSensitivity = mouseHandler.Sensitivity.GetBoundCopy();
+ localSensitivity = handlerSensitivity.GetUnboundCopy();
+ relativeMode = mouseHandler.UseRelativeMode.GetBoundCopy();
windowMode = config.GetBindable(FrameworkSetting.WindowMode);
- ignoredInputHandlers = config.GetBindable(FrameworkSetting.IgnoredInputHandlers);
Children = new Drawable[]
{
new SettingsCheckbox
{
- LabelText = "Raw input",
- Current = rawInputToggle
+ LabelText = "High precision mouse",
+ Current = relativeMode
},
new SensitivitySetting
{
LabelText = "Cursor sensitivity",
Current = localSensitivity
},
- new SettingsCheckbox
- {
- LabelText = "Map absolute input to window",
- Current = config.GetBindable(FrameworkSetting.MapAbsoluteInputToWindow)
- },
confineMouseModeSetting = new SettingsEnumDropdown
{
LabelText = "Confine mouse cursor to window",
@@ -76,7 +75,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
base.LoadComplete();
- configSensitivity.BindValueChanged(val =>
+ relativeMode.BindValueChanged(relative => localSensitivity.Disabled = !relative.NewValue, true);
+
+ handlerSensitivity.BindValueChanged(val =>
{
var disabled = localSensitivity.Disabled;
@@ -85,7 +86,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
localSensitivity.Disabled = disabled;
}, true);
- localSensitivity.BindValueChanged(val => configSensitivity.Value = val.NewValue);
+ localSensitivity.BindValueChanged(val => handlerSensitivity.Value = val.NewValue);
windowMode.BindValueChanged(mode =>
{
@@ -102,32 +103,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input
confineMouseModeSetting.TooltipText = string.Empty;
}
}, true);
-
- if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows)
- {
- rawInputToggle.Disabled = true;
- localSensitivity.Disabled = true;
- }
- else
- {
- rawInputToggle.ValueChanged += enabled =>
- {
- // this is temporary until we support per-handler settings.
- const string raw_mouse_handler = @"OsuTKRawMouseHandler";
- const string standard_mouse_handlers = @"OsuTKMouseHandler MouseHandler";
-
- ignoredInputHandlers.Value = enabled.NewValue ? standard_mouse_handlers : raw_mouse_handler;
- };
-
- ignoredInputHandlers.ValueChanged += handler =>
- {
- bool raw = !handler.NewValue.Contains("Raw");
- rawInputToggle.Value = raw;
- localSensitivity.Disabled = !raw;
- };
-
- ignoredInputHandlers.TriggerChange();
- }
}
private class SensitivitySetting : SettingsSlider
@@ -141,7 +116,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
private class SensitivitySlider : OsuSliderBar
{
- public override string TooltipText => Current.Disabled ? "enable raw input to adjust sensitivity" : $"{base.TooltipText}x";
+ public override string TooltipText => Current.Disabled ? "enable high precision mouse to adjust sensitivity" : $"{base.TooltipText}x";
}
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs
new file mode 100644
index 0000000000..ecb8acce54
--- /dev/null
+++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs
@@ -0,0 +1,185 @@
+// 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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Handlers.Tablet;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Settings.Sections.Input
+{
+ public class TabletAreaSelection : CompositeDrawable
+ {
+ private readonly ITabletHandler handler;
+
+ private Container tabletContainer;
+ private Container usableAreaContainer;
+
+ private readonly Bindable areaOffset = new Bindable();
+ private readonly Bindable areaSize = new Bindable();
+
+ private readonly IBindable tablet = new Bindable();
+
+ private OsuSpriteText tabletName;
+
+ private Box usableFill;
+ private OsuSpriteText usableAreaText;
+
+ public TabletAreaSelection(ITabletHandler handler)
+ {
+ this.handler = handler;
+
+ Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ InternalChild = tabletContainer = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Masking = true,
+ CornerRadius = 5,
+ BorderThickness = 2,
+ BorderColour = colour.Gray3,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colour.Gray1,
+ },
+ usableAreaContainer = new Container
+ {
+ Origin = Anchor.Centre,
+ Children = new Drawable[]
+ {
+ usableFill = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.6f,
+ },
+ new Box
+ {
+ Colour = Color4.White,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Height = 5,
+ },
+ new Box
+ {
+ Colour = Color4.White,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Width = 5,
+ },
+ usableAreaText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Colour = Color4.White,
+ Font = OsuFont.Default.With(size: 12),
+ Y = 10
+ }
+ }
+ },
+ tabletName = new OsuSpriteText
+ {
+ Padding = new MarginPadding(3),
+ Font = OsuFont.Default.With(size: 8)
+ },
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ areaOffset.BindTo(handler.AreaOffset);
+ areaOffset.BindValueChanged(val =>
+ {
+ usableAreaContainer.MoveTo(val.NewValue, 100, Easing.OutQuint)
+ .OnComplete(_ => checkBounds()); // required as we are using SSDQ.
+ }, true);
+
+ areaSize.BindTo(handler.AreaSize);
+ areaSize.BindValueChanged(val =>
+ {
+ usableAreaContainer.ResizeTo(val.NewValue, 100, Easing.OutQuint)
+ .OnComplete(_ => checkBounds()); // required as we are using SSDQ.
+
+ int x = (int)val.NewValue.X;
+ int y = (int)val.NewValue.Y;
+ int commonDivider = greatestCommonDivider(x, y);
+
+ usableAreaText.Text = $"{(float)x / commonDivider}:{(float)y / commonDivider}";
+ }, true);
+
+ tablet.BindTo(handler.Tablet);
+ tablet.BindValueChanged(_ => Scheduler.AddOnce(updateTabletDetails));
+
+ updateTabletDetails();
+ // initial animation should be instant.
+ FinishTransforms(true);
+ }
+
+ private void updateTabletDetails()
+ {
+ tabletContainer.Size = tablet.Value?.Size ?? Vector2.Zero;
+ tabletName.Text = tablet.Value?.Name ?? string.Empty;
+ checkBounds();
+ }
+
+ private static int greatestCommonDivider(int a, int b)
+ {
+ while (b != 0)
+ {
+ int remainder = a % b;
+ a = b;
+ b = remainder;
+ }
+
+ return a;
+ }
+
+ [Resolved]
+ private OsuColour colour { get; set; }
+
+ private void checkBounds()
+ {
+ if (tablet.Value == null)
+ return;
+
+ var usableSsdq = usableAreaContainer.ScreenSpaceDrawQuad;
+
+ bool isWithinBounds = tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.TopLeft + new Vector2(1)) &&
+ tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.BottomRight - new Vector2(1));
+
+ usableFill.FadeColour(isWithinBounds ? colour.Blue : colour.RedLight, 100);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (!(tablet.Value?.Size is Vector2 size))
+ return;
+
+ float fitX = size.X / (DrawWidth - Padding.Left - Padding.Right);
+ float fitY = size.Y / DrawHeight;
+
+ float adjust = MathF.Max(fitX, fitY);
+
+ tabletContainer.Scale = new Vector2(1 / adjust);
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
new file mode 100644
index 0000000000..bd0f7ddc4c
--- /dev/null
+++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
@@ -0,0 +1,285 @@
+// 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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Input.Handlers.Tablet;
+using osu.Framework.Platform;
+using osu.Framework.Threading;
+using osu.Game.Graphics.Sprites;
+using osuTK;
+
+namespace osu.Game.Overlays.Settings.Sections.Input
+{
+ public class TabletSettings : SettingsSubsection
+ {
+ private readonly ITabletHandler tabletHandler;
+
+ private readonly Bindable areaOffset = new Bindable();
+ private readonly Bindable areaSize = new Bindable();
+ private readonly IBindable tablet = new Bindable();
+
+ private readonly BindableNumber offsetX = new BindableNumber { MinValue = 0 };
+ private readonly BindableNumber offsetY = new BindableNumber { MinValue = 0 };
+
+ private readonly BindableNumber sizeX = new BindableNumber { MinValue = 10 };
+ private readonly BindableNumber sizeY = new BindableNumber { MinValue = 10 };
+
+ [Resolved]
+ private GameHost host { get; set; }
+
+ ///
+ /// Based on ultrawide monitor configurations.
+ ///
+ private const float largest_feasible_aspect_ratio = 21f / 9;
+
+ private readonly BindableNumber aspectRatio = new BindableFloat(1)
+ {
+ MinValue = 1 / largest_feasible_aspect_ratio,
+ MaxValue = largest_feasible_aspect_ratio,
+ Precision = 0.01f,
+ };
+
+ private readonly BindableBool aspectLock = new BindableBool();
+
+ private ScheduledDelegate aspectRatioApplication;
+
+ private FillFlowContainer mainSettings;
+
+ private OsuSpriteText noTabletMessage;
+
+ protected override string Header => "Tablet";
+
+ public TabletSettings(ITabletHandler tabletHandler)
+ {
+ this.tabletHandler = tabletHandler;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Children = new Drawable[]
+ {
+ new SettingsCheckbox
+ {
+ LabelText = "Enabled",
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Current = tabletHandler.Enabled
+ },
+ noTabletMessage = new OsuSpriteText
+ {
+ Text = "No tablet detected!",
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS }
+ },
+ mainSettings = new FillFlowContainer
+ {
+ Alpha = 0,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(0, 8),
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new TabletAreaSelection(tabletHandler)
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 300,
+ },
+ new DangerousSettingsButton
+ {
+ Text = "Reset to full area",
+ Action = () =>
+ {
+ aspectLock.Value = false;
+
+ areaOffset.SetDefault();
+ areaSize.SetDefault();
+ },
+ },
+ new SettingsButton
+ {
+ Text = "Conform to current game aspect ratio",
+ Action = () =>
+ {
+ forceAspectRatio((float)host.Window.ClientSize.Width / host.Window.ClientSize.Height);
+ }
+ },
+ new SettingsSlider
+ {
+ TransferValueOnCommit = true,
+ LabelText = "Aspect Ratio",
+ Current = aspectRatio
+ },
+ new SettingsSlider
+ {
+ TransferValueOnCommit = true,
+ LabelText = "X Offset",
+ Current = offsetX
+ },
+ new SettingsSlider
+ {
+ TransferValueOnCommit = true,
+ LabelText = "Y Offset",
+ Current = offsetY
+ },
+ new SettingsCheckbox
+ {
+ LabelText = "Lock aspect ratio",
+ Current = aspectLock
+ },
+ new SettingsSlider
+ {
+ TransferValueOnCommit = true,
+ LabelText = "Width",
+ Current = sizeX
+ },
+ new SettingsSlider
+ {
+ TransferValueOnCommit = true,
+ LabelText = "Height",
+ Current = sizeY
+ },
+ }
+ },
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ areaOffset.BindTo(tabletHandler.AreaOffset);
+ areaOffset.BindValueChanged(val =>
+ {
+ offsetX.Value = val.NewValue.X;
+ offsetY.Value = val.NewValue.Y;
+ }, true);
+
+ offsetX.BindValueChanged(val => areaOffset.Value = new Vector2(val.NewValue, areaOffset.Value.Y));
+ offsetY.BindValueChanged(val => areaOffset.Value = new Vector2(areaOffset.Value.X, val.NewValue));
+
+ areaSize.BindTo(tabletHandler.AreaSize);
+ areaSize.BindValueChanged(val =>
+ {
+ sizeX.Value = val.NewValue.X;
+ sizeY.Value = val.NewValue.Y;
+ }, true);
+
+ sizeX.BindValueChanged(val =>
+ {
+ areaSize.Value = new Vector2(val.NewValue, areaSize.Value.Y);
+
+ aspectRatioApplication?.Cancel();
+ aspectRatioApplication = Schedule(() => applyAspectRatio(sizeX));
+ });
+
+ sizeY.BindValueChanged(val =>
+ {
+ areaSize.Value = new Vector2(areaSize.Value.X, val.NewValue);
+
+ aspectRatioApplication?.Cancel();
+ aspectRatioApplication = Schedule(() => applyAspectRatio(sizeY));
+ });
+
+ updateAspectRatio();
+ aspectRatio.BindValueChanged(aspect =>
+ {
+ aspectRatioApplication?.Cancel();
+ aspectRatioApplication = Schedule(() => forceAspectRatio(aspect.NewValue));
+ });
+
+ tablet.BindTo(tabletHandler.Tablet);
+ tablet.BindValueChanged(val =>
+ {
+ Scheduler.AddOnce(toggleVisibility);
+
+ var tab = val.NewValue;
+
+ bool tabletFound = tab != null;
+ if (!tabletFound)
+ return;
+
+ offsetX.MaxValue = tab.Size.X;
+ offsetX.Default = tab.Size.X / 2;
+ sizeX.Default = sizeX.MaxValue = tab.Size.X;
+
+ offsetY.MaxValue = tab.Size.Y;
+ offsetY.Default = tab.Size.Y / 2;
+ sizeY.Default = sizeY.MaxValue = tab.Size.Y;
+
+ areaSize.Default = new Vector2(sizeX.Default, sizeY.Default);
+ }, true);
+ }
+
+ private void toggleVisibility()
+ {
+ bool tabletFound = tablet.Value != null;
+
+ if (!tabletFound)
+ {
+ mainSettings.Hide();
+ noTabletMessage.Show();
+ return;
+ }
+
+ mainSettings.Show();
+ noTabletMessage.Hide();
+ }
+
+ private void applyAspectRatio(BindableNumber sizeChanged)
+ {
+ try
+ {
+ if (!aspectLock.Value)
+ {
+ float proposedAspectRatio = currentAspectRatio;
+
+ if (proposedAspectRatio >= aspectRatio.MinValue && proposedAspectRatio <= aspectRatio.MaxValue)
+ {
+ // aspect ratio was in a valid range.
+ updateAspectRatio();
+ return;
+ }
+ }
+
+ // if lock is applied (or the specified values were out of range) aim to adjust the axis the user was not adjusting to conform.
+ if (sizeChanged == sizeX)
+ sizeY.Value = (int)(areaSize.Value.X / aspectRatio.Value);
+ else
+ sizeX.Value = (int)(areaSize.Value.Y * aspectRatio.Value);
+ }
+ finally
+ {
+ // cancel any event which may have fired while updating variables as a result of aspect ratio limitations.
+ // this avoids a potential feedback loop.
+ aspectRatioApplication?.Cancel();
+ }
+ }
+
+ private void forceAspectRatio(float aspectRatio)
+ {
+ aspectLock.Value = false;
+
+ int proposedHeight = (int)(sizeX.Value / aspectRatio);
+
+ if (proposedHeight < sizeY.MaxValue)
+ sizeY.Value = proposedHeight;
+ else
+ sizeX.Value = (int)(sizeY.Value * aspectRatio);
+
+ updateAspectRatio();
+
+ aspectRatioApplication?.Cancel();
+ aspectLock.Value = true;
+ }
+
+ private void updateAspectRatio() => aspectRatio.Value = currentAspectRatio;
+
+ private float currentAspectRatio => sizeX.Value / sizeY.Value;
+ }
+}
diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs
index b43453f53d..6e99891794 100644
--- a/osu.Game/Overlays/Settings/Sections/InputSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs
@@ -1,28 +1,106 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input.Handlers;
+using osu.Framework.Input.Handlers.Joystick;
+using osu.Framework.Input.Handlers.Midi;
+using osu.Framework.Input.Handlers.Mouse;
+using osu.Framework.Input.Handlers.Tablet;
+using osu.Framework.Platform;
using osu.Game.Overlays.Settings.Sections.Input;
namespace osu.Game.Overlays.Settings.Sections
{
public class InputSection : SettingsSection
{
+ private readonly KeyBindingPanel keyConfig;
+
public override string Header => "Input";
+ [Resolved]
+ private GameHost host { get; set; }
+
public override Drawable CreateIcon() => new SpriteIcon
{
Icon = FontAwesome.Solid.Keyboard
};
public InputSection(KeyBindingPanel keyConfig)
+ {
+ this.keyConfig = keyConfig;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
{
Children = new Drawable[]
{
- new MouseSettings(),
- new KeyboardSettings(keyConfig),
+ new BindingSettings(keyConfig),
};
+
+ foreach (var handler in host.AvailableInputHandlers)
+ {
+ var handlerSection = createSectionFor(handler);
+
+ if (handlerSection != null)
+ Add(handlerSection);
+ }
+ }
+
+ private SettingsSubsection createSectionFor(InputHandler handler)
+ {
+ SettingsSubsection section;
+
+ switch (handler)
+ {
+ // ReSharper disable once SuspiciousTypeConversion.Global (net standard fuckery)
+ case ITabletHandler th:
+ section = new TabletSettings(th);
+ break;
+
+ case MouseHandler mh:
+ section = new MouseSettings(mh);
+ break;
+
+ // whitelist the handlers which should be displayed to avoid any weird cases of users touching settings they shouldn't.
+ case JoystickHandler _:
+ case MidiHandler _:
+ section = new HandlerSection(handler);
+ break;
+
+ default:
+ return null;
+ }
+
+ return section;
+ }
+
+ private class HandlerSection : SettingsSubsection
+ {
+ private readonly InputHandler handler;
+
+ public HandlerSection(InputHandler handler)
+ {
+ this.handler = handler;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Children = new Drawable[]
+ {
+ new SettingsCheckbox
+ {
+ LabelText = "Enabled",
+ Current = handler.Enabled
+ },
+ };
+ }
+
+ protected override string Header => handler.Description;
}
}
}
diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs
index 1b82d973e9..6abf6283b9 100644
--- a/osu.Game/Overlays/Settings/SettingsSubsection.cs
+++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs
@@ -8,10 +8,12 @@ using osu.Game.Graphics.Sprites;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Testing;
using osu.Game.Graphics;
namespace osu.Game.Overlays.Settings
{
+ [ExcludeFromDynamicCompile]
public abstract class SettingsSubsection : FillFlowContainer, IHasFilterableChildren
{
protected override Container Content => FlowContent;
diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs
index f1270f750e..8f3274b2b5 100644
--- a/osu.Game/Overlays/SettingsPanel.cs
+++ b/osu.Game/Overlays/SettingsPanel.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Overlays
private const float sidebar_width = Sidebar.DEFAULT_WIDTH;
- protected const float WIDTH = 400;
+ public const float WIDTH = 400;
protected Container ContentContainer;
diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs
index 14aa3fe99a..e66a8c016c 100644
--- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs
+++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs
@@ -117,6 +117,10 @@ namespace osu.Game.Rulesets.UI
public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotSupportedException();
+ public void BindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
+
+ public void UnbindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
+
public BindableNumber Volume => throw new NotSupportedException();
public BindableNumber Balance => throw new NotSupportedException();
@@ -125,8 +129,6 @@ namespace osu.Game.Rulesets.UI
public BindableNumber Tempo => throw new NotSupportedException();
- public IBindable GetAggregate(AdjustableProperty type) => throw new NotSupportedException();
-
public IBindable AggregateVolume => throw new NotSupportedException();
public IBindable AggregateBalance => throw new NotSupportedException();
@@ -135,10 +137,6 @@ namespace osu.Game.Rulesets.UI
public IBindable AggregateTempo => throw new NotSupportedException();
- public void BindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
-
- public void UnbindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
-
public int PlaybackConcurrency
{
get => throw new NotSupportedException();
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 96ec9644b5..c7ee26c248 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -20,6 +20,7 @@ using osu.Game.IO.Archives;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring.Legacy;
@@ -152,9 +153,21 @@ namespace osu.Game.Scoring
}
int beatmapMaxCombo;
+ double accuracy = score.Accuracy;
if (score.IsLegacyScore)
{
+ if (score.RulesetID == 3)
+ {
+ // In osu!stable, a full-GREAT score has 100% accuracy in mania. Along with a full combo, the score becomes indistinguishable from a full-PERFECT score.
+ // To get around this, recalculate accuracy based on the hit statistics.
+ // Note: This cannot be applied universally to all legacy scores, as some rulesets (e.g. catch) group multiple judgements together.
+ double maxBaseScore = score.Statistics.Select(kvp => kvp.Value).Sum() * Judgement.ToNumericResult(HitResult.Perfect);
+ double baseScore = score.Statistics.Select(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value).Sum();
+ if (maxBaseScore > 0)
+ accuracy = baseScore / maxBaseScore;
+ }
+
// This score is guaranteed to be an osu!stable score.
// The combo must be determined through either the beatmap's max combo value or the difficulty calculator, as lazer's scoring has changed and the score statistics cannot be used.
if (score.Beatmap.MaxCombo == null)
@@ -171,7 +184,7 @@ namespace osu.Game.Scoring
difficultyBindable.BindValueChanged(d =>
{
if (d.NewValue is StarDifficulty diff)
- updateScore(diff.MaxCombo);
+ updateScore(diff.MaxCombo, accuracy);
}, true);
return;
@@ -186,10 +199,10 @@ namespace osu.Game.Scoring
beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetOrDefault(r)).Sum();
}
- updateScore(beatmapMaxCombo);
+ updateScore(beatmapMaxCombo, accuracy);
}
- private void updateScore(int beatmapMaxCombo)
+ private void updateScore(int beatmapMaxCombo, double accuracy)
{
if (beatmapMaxCombo == 0)
{
@@ -202,7 +215,7 @@ namespace osu.Game.Scoring
scoreProcessor.Mods.Value = score.Mods;
- Value = (long)Math.Round(scoreProcessor.GetScore(ScoringMode.Value, beatmapMaxCombo, score.Accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics));
+ Value = (long)Math.Round(scoreProcessor.GetScore(ScoringMode.Value, beatmapMaxCombo, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics));
}
}
diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs
index d9477dd4bc..ff33f0c70d 100644
--- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs
+++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs
@@ -4,7 +4,6 @@
using System;
using System.Linq;
using osu.Framework.Bindables;
-using osu.Framework.Graphics.Colour;
using osu.Game.Graphics;
using osuTK.Graphics;
@@ -48,7 +47,7 @@ namespace osu.Game.Screens.Edit
/// The beat divisor.
/// The set of colours.
/// The applicable colour from for .
- public static ColourInfo GetColourFor(int beatDivisor, OsuColour colours)
+ public static Color4 GetColourFor(int beatDivisor, OsuColour colours)
{
switch (beatDivisor)
{
diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs
index 9739f2876a..bdc6e238c8 100644
--- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs
+++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Screens.Edit.Components
[Resolved]
private EditorClock editorClock { get; set; }
- private readonly BindableNumber tempo = new BindableDouble(1);
+ private readonly BindableNumber freqAdjust = new BindableDouble(1);
[BackgroundDependencyLoader]
private void load()
@@ -58,16 +58,16 @@ namespace osu.Game.Screens.Edit.Components
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Padding = new MarginPadding { Left = 45 },
- Child = new PlaybackTabControl { Current = tempo },
+ Child = new PlaybackTabControl { Current = freqAdjust },
}
};
- Track.BindValueChanged(tr => tr.NewValue?.AddAdjustment(AdjustableProperty.Tempo, tempo), true);
+ Track.BindValueChanged(tr => tr.NewValue?.AddAdjustment(AdjustableProperty.Frequency, freqAdjust), true);
}
protected override void Dispose(bool isDisposing)
{
- Track.Value?.RemoveAdjustment(AdjustableProperty.Tempo, tempo);
+ Track.Value?.RemoveAdjustment(AdjustableProperty.Frequency, freqAdjust);
base.Dispose(isDisposing);
}
@@ -101,7 +101,7 @@ namespace osu.Game.Screens.Edit.Components
private class PlaybackTabControl : OsuTabControl
{
- private static readonly double[] tempo_values = { 0.5, 0.75, 1 };
+ private static readonly double[] tempo_values = { 0.25, 0.5, 0.75, 1 };
protected override TabItem CreateTabItem(double value) => new PlaybackTabItem(value);
diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
index 051d0766bf..7def7e1d16 100644
--- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
@@ -338,7 +338,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
private bool beginClickSelection(MouseButtonEvent e)
{
// Iterate from the top of the input stack (blueprints closest to the front of the screen first).
- foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Reverse())
+ // Priority is given to already-selected blueprints.
+ foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Reverse().OrderByDescending(b => b.IsSelected))
{
if (!blueprint.IsHovered) continue;
diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs
index 8a92a2011d..59f88ac641 100644
--- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs
@@ -132,7 +132,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
var colour = BindableBeatDivisor.GetColourFor(BindableBeatDivisor.GetDivisorForBeatIndex(beatIndex + placementIndex + 1, beatDivisor.Value), Colours);
int repeatIndex = placementIndex / beatDivisor.Value;
- return colour.MultiplyAlpha(0.5f / (repeatIndex + 1));
+ return ColourInfo.SingleColour(colour).MultiplyAlpha(0.5f / (repeatIndex + 1));
}
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs
index 2f4721f63e..9d6b44e207 100644
--- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs
@@ -113,16 +113,25 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (e.Repeat || !e.ControlPressed)
return false;
+ bool runOperationFromHotkey(Func operation)
+ {
+ operationStarted();
+ bool result = operation?.Invoke() ?? false;
+ operationEnded();
+
+ return result;
+ }
+
switch (e.Key)
{
case Key.G:
- return CanReverse && OnReverse?.Invoke() == true;
+ return CanReverse && runOperationFromHotkey(OnReverse);
case Key.H:
- return CanScaleX && OnFlip?.Invoke(Direction.Horizontal) == true;
+ return CanScaleX && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Horizontal) ?? false);
case Key.J:
- return CanScaleY && OnFlip?.Invoke(Direction.Vertical) == true;
+ return CanScaleY && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Vertical) ?? false);
}
return base.OnKeyDown(e);
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
index 666026e05e..0697dbb392 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
@@ -177,7 +177,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
if (!track.IsLoaded)
return;
- editorClock.Seek(Current / Content.DrawWidth * track.Length);
+ double target = Current / Content.DrawWidth * track.Length;
+ editorClock.Seek(Math.Min(track.Length, target));
}
private void scrollToTrackTime()
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs
index fb11b859a7..c070c833f8 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs
@@ -6,7 +6,9 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Caching;
+using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
@@ -124,25 +126,28 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
if (beat == 0 && i == 0)
nextMinTick = float.MinValue;
- var indexInBar = beat % ((int)point.TimeSignature * beatDivisor.Value);
+ int indexInBar = beat % ((int)point.TimeSignature * beatDivisor.Value);
var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value);
var colour = BindableBeatDivisor.GetColourFor(divisor, colours);
+ bool isMainBeat = indexInBar == 0;
+
// even though "bar lines" take up the full vertical space, we render them in two pieces because it allows for less anchor/origin churn.
- var height = indexInBar == 0 ? 0.5f : 0.1f - (float)divisor / highestDivisor * 0.08f;
+ float height = isMainBeat ? 0.5f : 0.4f - (float)divisor / highestDivisor * 0.2f;
+ float gradientOpacity = isMainBeat ? 1 : 0;
var topPoint = getNextUsablePoint();
topPoint.X = xPos;
- topPoint.Colour = colour;
topPoint.Height = height;
+ topPoint.Colour = ColourInfo.GradientVertical(colour, colour.Opacity(gradientOpacity));
topPoint.Anchor = Anchor.TopLeft;
topPoint.Origin = Anchor.TopCentre;
var bottomPoint = getNextUsablePoint();
bottomPoint.X = xPos;
- bottomPoint.Colour = colour;
bottomPoint.Anchor = Anchor.BottomLeft;
+ bottomPoint.Colour = ColourInfo.GradientVertical(colour.Opacity(gradientOpacity), colour);
bottomPoint.Origin = Anchor.BottomCentre;
bottomPoint.Height = height;
}
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 0ba202b082..0c24eb6a4d 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -74,7 +74,6 @@ namespace osu.Game.Screens.Edit
private string lastSavedHash;
- private Box bottomBackground;
private Container screenContainer;
private EditorScreen currentScreen;
@@ -106,26 +105,29 @@ namespace osu.Game.Screens.Edit
[BackgroundDependencyLoader]
private void load(OsuColour colours, GameHost host, OsuConfigManager config)
{
- if (Beatmap.Value is DummyWorkingBeatmap)
+ var loadableBeatmap = Beatmap.Value;
+
+ if (loadableBeatmap is DummyWorkingBeatmap)
{
isNewBeatmap = true;
- var newBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value);
+ loadableBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value);
+
+ // required so we can get the track length in EditorClock.
+ // this is safe as nothing has yet got a reference to this new beatmap.
+ loadableBeatmap.LoadTrack();
// this is a bit haphazard, but guards against setting the lease Beatmap bindable if
// the editor has already been exited.
if (!ValidForPush)
return;
-
- // this probably shouldn't be set in the asynchronous load method, but everything following relies on it.
- Beatmap.Value = newBeatmap;
}
- beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor;
- beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
+ beatDivisor.Value = loadableBeatmap.BeatmapInfo.BeatDivisor;
+ beatDivisor.BindValueChanged(divisor => loadableBeatmap.BeatmapInfo.BeatDivisor = divisor.NewValue);
// Todo: should probably be done at a DrawableRuleset level to share logic with Player.
- clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false };
+ clock = new EditorClock(loadableBeatmap, beatDivisor) { IsCoupled = false };
UpdateClockSource();
@@ -139,7 +141,7 @@ namespace osu.Game.Screens.Edit
try
{
- playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset);
+ playableBeatmap = loadableBeatmap.GetPlayableBeatmap(loadableBeatmap.BeatmapInfo.Ruleset);
// clone these locally for now to avoid incurring overhead on GetPlayableBeatmap usages.
// eventually we will want to improve how/where this is done as there are issues with *not* cloning it in all cases.
@@ -153,13 +155,21 @@ namespace osu.Game.Screens.Edit
return;
}
- AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, Beatmap.Value.Skin));
+ AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, loadableBeatmap.Skin));
dependencies.CacheAs(editorBeatmap);
changeHandler = new EditorChangeHandler(editorBeatmap);
dependencies.CacheAs(changeHandler);
updateLastSavedHash();
+ Schedule(() =>
+ {
+ // we need to avoid changing the beatmap from an asynchronous load thread. it can potentially cause weirdness including crashes.
+ // this assumes that nothing during the rest of this load() method is accessing Beatmap.Value (loadableBeatmap should be preferred).
+ // generally this is quite safe, as the actual load of editor content comes after menuBar.Mode.ValueChanged is fired in its own LoadComplete.
+ Beatmap.Value = loadableBeatmap;
+ });
+
OsuMenuItem undoMenuItem;
OsuMenuItem redoMenuItem;
@@ -167,17 +177,6 @@ namespace osu.Game.Screens.Edit
EditorMenuItem copyMenuItem;
EditorMenuItem pasteMenuItem;
- var fileMenuItems = new List