diff --git a/README.md b/README.md
index ad56178132..856536d22d 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,9 @@ This is still heavily under development and is not intended for end-user use. Th
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted.
-Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu-framework/issues).
+Please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**.
+
+Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues).
Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible.
diff --git a/appveyor.yml b/appveyor.yml
index 9048428590..9cf68803a2 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -9,17 +9,17 @@ cache:
- inspectcode -> appveyor.yml
- packages -> **\packages.config
install:
- - cmd: git submodule update --init --recursive
+ - cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y
- cmd: choco install nvika -y
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.3/CodeFileSanity.exe
before_build:
- cmd: CodeFileSanity.exe
- - cmd: nuget restore
+ - cmd: nuget restore -verbosity quiet
build:
project: osu.sln
parallel: true
verbosity: minimal
after_build:
- - cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln
+ - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
\ No newline at end of file
diff --git a/osu.Desktop.Deploy/App.config b/osu.Desktop.Deploy/App.config
index 42b2d34097..8218f31a68 100644
--- a/osu.Desktop.Deploy/App.config
+++ b/osu.Desktop.Deploy/App.config
@@ -13,7 +13,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-
+
diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs
index 1be4bdeb0c..48cc9c7581 100644
--- a/osu.Desktop.Deploy/Program.cs
+++ b/osu.Desktop.Deploy/Program.cs
@@ -145,6 +145,8 @@ namespace osu.Desktop.Deploy
///
private static void checkReleaseFiles()
{
+ if (!canGitHub) return;
+
var releaseLines = getReleaseLines();
//ensure we have all files necessary
@@ -157,6 +159,8 @@ namespace osu.Desktop.Deploy
private static void pruneReleases()
{
+ if (!canGitHub) return;
+
write("Pruning RELEASES...");
var releaseLines = getReleaseLines().ToList();
@@ -190,7 +194,7 @@ namespace osu.Desktop.Deploy
private static void uploadBuild(string version)
{
- if (string.IsNullOrEmpty(GitHubAccessToken) || string.IsNullOrEmpty(codeSigningCertPath))
+ if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate))
return;
write("Publishing to GitHub...");
@@ -228,8 +232,12 @@ namespace osu.Desktop.Deploy
private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage);
+ private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken);
+
private static void checkGitHubReleases()
{
+ if (!canGitHub) return;
+
write("Checking GitHub releases...");
var req = new JsonWebRequest>($"{GitHubApiEndpoint}");
req.AuthenticatedBlockingPerform();
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
index 0e4935aa7a..6b9ec8b9a4 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
@@ -11,14 +11,14 @@ using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
- internal class CatchBeatmapConverter : BeatmapConverter
+ internal class CatchBeatmapConverter : BeatmapConverter
{
protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
- protected override IEnumerable ConvertHitObject(HitObject obj, Beatmap beatmap)
+ protected override IEnumerable ConvertHitObject(HitObject obj, Beatmap beatmap)
{
var curveData = obj as IHasCurve;
- var positionData = obj as IHasPosition;
+ var positionData = obj as IHasXPosition;
var comboData = obj as IHasCombo;
if (positionData == null)
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
index 7fac19d135..b2f7fdabfc 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
@@ -6,9 +6,9 @@ using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
- internal class CatchBeatmapProcessor : BeatmapProcessor
+ internal class CatchBeatmapProcessor : BeatmapProcessor
{
- public override void PostProcess(Beatmap beatmap)
+ public override void PostProcess(Beatmap beatmap)
{
if (beatmap.ComboColors.Count == 0)
return;
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
int comboIndex = 0;
int colourIndex = 0;
- CatchBaseHit lastObj = null;
+ CatchHitObject lastObj = null;
foreach (var obj in beatmap.HitObjects)
{
diff --git a/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs
index b77be9d1f0..e9524a867d 100644
--- a/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs
@@ -8,14 +8,14 @@ using System.Collections.Generic;
namespace osu.Game.Rulesets.Catch
{
- public class CatchDifficultyCalculator : DifficultyCalculator
+ public class CatchDifficultyCalculator : DifficultyCalculator
{
public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap)
{
}
- public override double Calculate(Dictionary categoryDifficulty = null) => 0;
+ public override double Calculate(Dictionary categoryDifficulty = null) => 0;
- protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
+ protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
similarity index 55%
rename from osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs
rename to osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index 2f33cf1093..cb4e6453ce 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -1,14 +1,18 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects
{
- public abstract class CatchBaseHit : HitObject, IHasXPosition, IHasCombo
+ public abstract class CatchHitObject : HitObject, IHasXPosition, IHasCombo
{
+ public const double OBJECT_RADIUS = 44;
+
public float X { get; set; }
public Color4 ComboColour { get; set; } = Color4.Gray;
@@ -20,5 +24,14 @@ namespace osu.Game.Rulesets.Catch.Objects
/// The next fruit starts a new combo. Used for explodey.
///
public virtual bool LastInCombo { get; set; }
+
+ public float Scale { get; set; } = 1;
+
+ public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ {
+ base.ApplyDefaults(controlPointInfo, difficulty);
+
+ Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
index e057bf3d8e..b90a06b94e 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
@@ -5,11 +5,12 @@ using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
+using OpenTK;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public abstract class DrawableCatchHitObject : DrawableCatchHitObject
- where TObject : CatchBaseHit
+ where TObject : CatchHitObject
{
public new TObject HitObject;
@@ -17,12 +18,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
: base(hitObject)
{
HitObject = hitObject;
+
+ Scale = new Vector2(HitObject.Scale);
}
}
- public abstract class DrawableCatchHitObject : DrawableScrollingHitObject
+ public abstract class DrawableCatchHitObject : DrawableScrollingHitObject
{
- protected DrawableCatchHitObject(CatchBaseHit hitObject)
+ protected DrawableCatchHitObject(CatchHitObject hitObject)
: base(hitObject)
{
RelativePositionAxes = Axes.Both;
@@ -30,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Y = (float)HitObject.StartTime;
}
- public Func CheckPosition;
+ public Func CheckPosition;
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
index afda91d0b4..bfb674d1b4 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
};
- foreach (CatchBaseHit tick in s.Ticks)
+ foreach (CatchHitObject tick in s.Ticks)
{
TinyDroplet tiny = tick as TinyDroplet;
if (tiny != null)
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
}
}
- protected override void AddNested(DrawableHitObject h)
+ protected override void AddNested(DrawableHitObject h)
{
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
dropletContainer.Add(h);
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
index 00ddd365e3..2de266b3f0 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
{
public class Pulp : Circle, IHasAccentColour
{
- public const float PULP_SIZE = 20;
+ public const float PULP_SIZE = (float)CatchHitObject.OBJECT_RADIUS / 2.2f;
public Pulp()
{
diff --git a/osu.Game.Rulesets.Catch/Objects/Droplet.cs b/osu.Game.Rulesets.Catch/Objects/Droplet.cs
index b1206e0d75..a2bdf830e5 100644
--- a/osu.Game.Rulesets.Catch/Objects/Droplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Droplet.cs
@@ -3,7 +3,7 @@
namespace osu.Game.Rulesets.Catch.Objects
{
- public class Droplet : CatchBaseHit
+ public class Droplet : CatchHitObject
{
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Fruit.cs b/osu.Game.Rulesets.Catch/Objects/Fruit.cs
index fc55f83969..5f1060fb51 100644
--- a/osu.Game.Rulesets.Catch/Objects/Fruit.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Fruit.cs
@@ -3,7 +3,7 @@
namespace osu.Game.Rulesets.Catch.Objects
{
- public class Fruit : CatchBaseHit
+ public class Fruit : CatchHitObject
{
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index 6462f6f6a8..bf9f0bd44b 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -15,7 +15,7 @@ using osu.Framework.Lists;
namespace osu.Game.Rulesets.Catch.Objects
{
- public class JuiceStream : CatchBaseHit, IHasCurve
+ public class JuiceStream : CatchHitObject, IHasCurve
{
///
/// Positional distance that results in a duration of one second, before any speed adjustments.
@@ -42,11 +42,11 @@ namespace osu.Game.Rulesets.Catch.Objects
TickDistance = scoringDistance / difficulty.SliderTickRate;
}
- public IEnumerable Ticks
+ public IEnumerable Ticks
{
get
{
- SortedList ticks = new SortedList((a, b) => a.StartTime.CompareTo(b.StartTime));
+ SortedList ticks = new SortedList((a, b) => a.StartTime.CompareTo(b.StartTime));
if (TickDistance == 0)
return ticks;
diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index 66a5636b74..0806c4b29d 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -10,14 +10,14 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.Scoring
{
- internal class CatchScoreProcessor : ScoreProcessor
+ internal class CatchScoreProcessor : ScoreProcessor
{
- public CatchScoreProcessor(RulesetContainer rulesetContainer)
+ public CatchScoreProcessor(RulesetContainer rulesetContainer)
: base(rulesetContainer)
{
}
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void SimulateAutoplay(Beatmap beatmap)
{
foreach (var obj in beatmap.HitObjects)
{
diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs
index a890a8a386..586de17f15 100644
--- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs
+++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs
@@ -11,16 +11,26 @@ namespace osu.Game.Rulesets.Catch.Tests
[Ignore("getting CI working")]
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
{
- public TestCaseCatchStacker() : base(typeof(CatchRuleset))
+ public TestCaseCatchStacker()
+ : base(typeof(CatchRuleset))
{
}
protected override Beatmap CreateBeatmap()
{
- var beatmap = new Beatmap();
+ var beatmap = new Beatmap
+ {
+ BeatmapInfo = new BeatmapInfo
+ {
+ BaseDifficulty = new BeatmapDifficulty
+ {
+ CircleSize = 6,
+ }
+ }
+ };
- for (int i = 0; i < 256; i++)
- beatmap.HitObjects.Add(new Fruit { X = 0.5f, StartTime = i * 100, NewCombo = i % 8 == 0 });
+ for (int i = 0; i < 512; i++)
+ beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });
return beatmap;
}
diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcher.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcher.cs
deleted file mode 100644
index 341612b760..0000000000
--- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcher.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Collections.Generic;
-using NUnit.Framework;
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Game.Rulesets.Catch.UI;
-using osu.Game.Tests.Visual;
-using OpenTK;
-
-namespace osu.Game.Rulesets.Catch.Tests
-{
- [TestFixture]
- [Ignore("getting CI working")]
- internal class TestCaseCatcher : OsuTestCase
- {
- public override IReadOnlyList RequiredTypes => new[]
- {
- typeof(Catcher),
- };
-
- [BackgroundDependencyLoader]
- private void load(RulesetStore rulesets)
- {
- Children = new Drawable[]
- {
- new CatchInputManager(rulesets.GetRuleset(2))
- {
- RelativeSizeAxes = Axes.Both,
- Child = new Catcher
- {
- RelativePositionAxes = Axes.Both,
- RelativeSizeAxes = Axes.Both,
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- Size = new Vector2(1, 0.2f),
- }
- },
- };
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs
new file mode 100644
index 0000000000..538f6930ed
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs
@@ -0,0 +1,50 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Catch.Tests
+{
+ [TestFixture]
+ [Ignore("getting CI working")]
+ internal class TestCaseCatcherArea : OsuTestCase
+ {
+ private RulesetInfo catchRuleset;
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(CatcherArea),
+ };
+
+ public TestCaseCatcherArea()
+ {
+ AddSliderStep("CircleSize", 0, 8, 5, createCatcher);
+ }
+
+ private void createCatcher(float size)
+ {
+ Child = new CatchInputManager(catchRuleset)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new CatcherArea(new BeatmapDifficulty { CircleSize = size })
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.BottomLeft
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ catchRuleset = rulesets.GetRuleset(2);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs
new file mode 100644
index 0000000000..0d2dc14160
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs
@@ -0,0 +1,16 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+
+namespace osu.Game.Rulesets.Catch.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
+ {
+ public TestCasePerformancePoints()
+ : base(new CatchRuleset(new RulesetInfo()))
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index 987eef5e45..6fd0793500 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -1,11 +1,11 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.UI;
using OpenTK;
using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Judgements;
@@ -20,10 +20,9 @@ namespace osu.Game.Rulesets.Catch.UI
protected override Container Content => content;
private readonly Container content;
- private readonly Container catcherContainer;
- private readonly Catcher catcher;
+ private readonly CatcherArea catcherArea;
- public CatchPlayfield()
+ public CatchPlayfield(BeatmapDifficulty difficulty)
: base(Axes.Y)
{
Container explodingFruitContainer;
@@ -43,30 +42,16 @@ namespace osu.Game.Rulesets.Catch.UI
{
RelativeSizeAxes = Axes.Both,
},
- catcherContainer = new Container
+ catcherArea = new CatcherArea(difficulty)
{
- RelativeSizeAxes = Axes.X,
+ ExplodingFruitTarget = explodingFruitContainer,
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft,
- Height = 180,
- Child = catcher = new Catcher
- {
- ExplodingFruitTarget = explodingFruitContainer,
- RelativePositionAxes = Axes.Both,
- Origin = Anchor.TopCentre,
- X = 0.5f,
- }
}
};
}
- protected override void Update()
- {
- base.Update();
- catcher.Size = new Vector2(catcherContainer.DrawSize.Y);
- }
-
- public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2;
+ public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.CanCatch(obj);
public override void Add(DrawableHitObject h)
{
@@ -88,7 +73,7 @@ namespace osu.Game.Rulesets.Catch.UI
(judgedObject.Parent as Container)?.Remove(judgedObject);
(judgedObject.Parent as Container)?.Remove(judgedObject);
- catcher.Add(judgedObject, screenPosition);
+ catcherArea.Add(judgedObject, screenPosition);
}
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
index 92912eb177..3ed9090098 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
@@ -13,7 +13,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.UI
{
- public class CatchRulesetContainer : ScrollingRulesetContainer
+ public class CatchRulesetContainer : ScrollingRulesetContainer
{
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
@@ -22,15 +22,15 @@ namespace osu.Game.Rulesets.Catch.UI
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
- protected override BeatmapProcessor CreateBeatmapProcessor() => new CatchBeatmapProcessor();
+ protected override BeatmapProcessor CreateBeatmapProcessor() => new CatchBeatmapProcessor();
- protected override BeatmapConverter CreateBeatmapConverter() => new CatchBeatmapConverter();
+ protected override BeatmapConverter CreateBeatmapConverter() => new CatchBeatmapConverter();
- protected override Playfield CreatePlayfield() => new CatchPlayfield();
+ protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty);
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
- protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h)
+ protected override DrawableHitObject GetVisualRepresentation(CatchHitObject h)
{
var fruit = h as Fruit;
if (fruit != null)
diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs
deleted file mode 100644
index 87fe95ed2f..0000000000
--- a/osu.Game.Rulesets.Catch/UI/Catcher.cs
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Linq;
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
-using osu.Framework.Input.Bindings;
-using osu.Framework.MathUtils;
-using osu.Game.Rulesets.Catch.Objects;
-using osu.Game.Rulesets.Objects.Drawables;
-using OpenTK;
-
-namespace osu.Game.Rulesets.Catch.UI
-{
- public class Catcher : Container, IKeyBindingHandler
- {
- private Texture texture;
-
- private Container caughtFruit;
-
- public Container ExplodingFruitTarget;
-
- [BackgroundDependencyLoader]
- private void load(TextureStore textures)
- {
- texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
-
- Children = new Drawable[]
- {
- createCatcherSprite(),
- caughtFruit = new Container
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.BottomCentre,
- }
- };
- }
-
- private int currentDirection;
-
- private bool dashing;
-
- protected bool Dashing
- {
- get { return dashing; }
- set
- {
- if (value == dashing) return;
-
- dashing = value;
-
- if (dashing)
- Schedule(addAdditiveSprite);
- }
- }
-
- private void addAdditiveSprite()
- {
- if (!dashing) return;
-
- var additive = createCatcherSprite();
-
- additive.RelativePositionAxes = Axes.Both;
- additive.Blending = BlendingMode.Additive;
- additive.Position = Position;
- additive.Scale = Scale;
-
- ((Container)Parent).Add(additive);
-
- additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
-
- Scheduler.AddDelayed(addAdditiveSprite, 50);
- }
-
- private Sprite createCatcherSprite() => new Sprite
- {
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fit,
- Texture = texture,
- OriginPosition = new Vector2(DrawWidth / 2, 10) //temporary until the sprite is aligned correctly.
- };
-
- public bool OnPressed(CatchAction action)
- {
- switch (action)
- {
- case CatchAction.MoveLeft:
- currentDirection--;
- return true;
- case CatchAction.MoveRight:
- currentDirection++;
- return true;
- case CatchAction.Dash:
- Dashing = true;
- return true;
- }
-
- return false;
- }
-
- public bool OnReleased(CatchAction action)
- {
- switch (action)
- {
- case CatchAction.MoveLeft:
- currentDirection++;
- return true;
- case CatchAction.MoveRight:
- currentDirection--;
- return true;
- case CatchAction.Dash:
- Dashing = false;
- return true;
- }
-
- return false;
- }
-
- ///
- /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
- ///
- private const double base_speed = 1.0 / 512;
-
- protected override void Update()
- {
- base.Update();
-
- if (currentDirection == 0) return;
-
- double dashModifier = Dashing ? 1 : 0.5;
-
- Scale = new Vector2(Math.Sign(currentDirection), 1);
- X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * base_speed * dashModifier, 0, 1);
- }
-
- public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
- {
- fruit.RelativePositionAxes = Axes.None;
- fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0);
-
- fruit.Anchor = Anchor.TopCentre;
- fruit.Origin = Anchor.BottomCentre;
- fruit.Scale *= 0.7f;
- fruit.LifetimeEnd = double.MaxValue;
-
- float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
-
- while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
- {
- fruit.X += RNG.Next(-5, 5);
- fruit.Y -= RNG.Next(0, 5);
- }
-
- caughtFruit.Add(fruit);
-
- if (((CatchBaseHit)fruit.HitObject).LastInCombo)
- explode();
- }
-
- private void explode()
- {
- var fruit = caughtFruit.ToArray();
-
- foreach (var f in fruit)
- {
- var originalX = f.X * Scale.X;
-
- if (ExplodingFruitTarget != null)
- {
- f.Anchor = Anchor.TopLeft;
- f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
-
- caughtFruit.Remove(f);
-
- ExplodingFruitTarget.Add(f);
- }
-
- f.MoveToY(f.Y - 50, 250, Easing.OutSine)
- .Then()
- .MoveToY(f.Y + 50, 500, Easing.InSine);
-
- f.MoveToX(f.X + originalX * 6, 1000);
- f.FadeOut(750);
-
- f.Expire();
- }
- }
- }
-}
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
new file mode 100644
index 0000000000..203db1bb8c
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -0,0 +1,240 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Input.Bindings;
+using osu.Framework.MathUtils;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Catch.UI
+{
+ public class CatcherArea : Container
+ {
+ public const float CATCHER_SIZE = 172;
+
+ private readonly Catcher catcher;
+
+ public Container ExplodingFruitTarget
+ {
+ set { catcher.ExplodingFruitTarget = value; }
+ }
+
+ public CatcherArea(BeatmapDifficulty difficulty = null)
+ {
+ RelativeSizeAxes = Axes.X;
+ Height = CATCHER_SIZE;
+ Child = catcher = new Catcher(difficulty)
+ {
+ AdditiveTarget = this,
+ };
+ }
+
+ public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
+ {
+ fruit.RelativePositionAxes = Axes.None;
+ fruit.Position = new Vector2(catcher.ToLocalSpace(absolutePosition).X - catcher.DrawSize.X / 2, 0);
+
+ fruit.Anchor = Anchor.TopCentre;
+ fruit.Origin = Anchor.BottomCentre;
+ fruit.Scale *= 0.7f;
+ fruit.LifetimeEnd = double.MaxValue;
+
+ catcher.Add(fruit);
+ }
+
+ public bool CanCatch(CatchHitObject obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X * Math.Abs(catcher.Scale.X) / DrawSize.X / 2;
+
+ public class Catcher : Container, IKeyBindingHandler
+ {
+ private Texture texture;
+
+ private Container caughtFruit;
+
+ public Container ExplodingFruitTarget;
+
+ public Container AdditiveTarget;
+
+ public Catcher(BeatmapDifficulty difficulty = null)
+ {
+ RelativePositionAxes = Axes.X;
+ X = 0.5f;
+
+ Origin = Anchor.TopCentre;
+ Anchor = Anchor.TopLeft;
+
+ Size = new Vector2(CATCHER_SIZE);
+ if (difficulty != null)
+ Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
+
+ Children = new Drawable[]
+ {
+ createCatcherSprite(),
+ caughtFruit = new Container
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.BottomCentre,
+ }
+ };
+ }
+
+ private int currentDirection;
+
+ private bool dashing;
+
+ protected bool Dashing
+ {
+ get { return dashing; }
+ set
+ {
+ if (value == dashing) return;
+
+ dashing = value;
+
+ if (dashing)
+ Schedule(addAdditiveSprite);
+ }
+ }
+
+ private void addAdditiveSprite()
+ {
+ if (!dashing || AdditiveTarget == null) return;
+
+ var additive = createCatcherSprite();
+
+ additive.Anchor = Anchor;
+ additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly.
+ additive.Position = Position;
+ additive.Scale = Scale;
+ additive.RelativePositionAxes = RelativePositionAxes;
+ additive.Blending = BlendingMode.Additive;
+
+ AdditiveTarget.Add(additive);
+
+ additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
+
+ Scheduler.AddDelayed(addAdditiveSprite, 50);
+ }
+
+ private Sprite createCatcherSprite() => new Sprite
+ {
+ Size = new Vector2(CATCHER_SIZE),
+ FillMode = FillMode.Fill,
+ Texture = texture,
+ OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly.
+ };
+
+ public void Add(DrawableHitObject fruit)
+ {
+ float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
+
+ while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
+ {
+ fruit.X += RNG.Next(-5, 5);
+ fruit.Y -= RNG.Next(0, 5);
+ }
+
+ caughtFruit.Add(fruit);
+
+ if (((CatchHitObject)fruit.HitObject).LastInCombo)
+ explode();
+ }
+
+ public bool OnPressed(CatchAction action)
+ {
+ switch (action)
+ {
+ case CatchAction.MoveLeft:
+ currentDirection--;
+ return true;
+ case CatchAction.MoveRight:
+ currentDirection++;
+ return true;
+ case CatchAction.Dash:
+ Dashing = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool OnReleased(CatchAction action)
+ {
+ switch (action)
+ {
+ case CatchAction.MoveLeft:
+ currentDirection++;
+ return true;
+ case CatchAction.MoveRight:
+ currentDirection--;
+ return true;
+ case CatchAction.Dash:
+ Dashing = false;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
+ ///
+ public const double BASE_SPEED = 1.0 / 512;
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (currentDirection == 0) return;
+
+ double dashModifier = Dashing ? 1 : 0.5;
+
+ Scale = new Vector2(Math.Abs(Scale.X) * Math.Sign(currentDirection), Scale.Y);
+ X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
+ }
+
+ private void explode()
+ {
+ var fruit = caughtFruit.ToArray();
+
+ foreach (var f in fruit)
+ {
+ var originalX = f.X * Scale.X;
+
+ if (ExplodingFruitTarget != null)
+ {
+ f.Anchor = Anchor.TopLeft;
+ f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
+
+ caughtFruit.Remove(f);
+
+ ExplodingFruitTarget.Add(f);
+ }
+
+ f.MoveToY(f.Y - 50, 250, Easing.OutSine)
+ .Then()
+ .MoveToY(f.Y + 50, 500, Easing.InSine);
+
+ f.MoveToX(f.X + originalX * 6, 1000);
+ f.FadeOut(750);
+
+ f.Expire();
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index f6d30ad3fa..d5a799b4ed 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -138,8 +138,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
Pattern newPattern = conversion.Generate();
lastPattern = newPattern;
- var stairPatternGenerator = (HitObjectPatternGenerator)conversion;
- lastStair = stairPatternGenerator.StairType;
+ var stairPatternGenerator = conversion as HitObjectPatternGenerator;
+ lastStair = stairPatternGenerator?.StairType ?? lastStair;
return newPattern.HitObjects;
}
diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs
index 67bc347535..e0763284a6 100644
--- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania
{
}
- public override double Calculate(Dictionary categoryDifficulty = null) => 0;
+ public override double Calculate(Dictionary categoryDifficulty = null) => 0;
protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize)));
}
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
index 164309c227..dfc9993bde 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs
@@ -176,22 +176,10 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModAutoplay : ModAutoplay
{
- private int availableColumns;
-
- public override void ApplyToRulesetContainer(RulesetContainer rulesetContainer)
- {
- // Todo: This shouldn't be done, we should be getting a ManiaBeatmap which should store AvailableColumns
- // But this is dependent on a _lot_ of refactoring
- var maniaRulesetContainer = (ManiaRulesetContainer)rulesetContainer;
- availableColumns = maniaRulesetContainer.AvailableColumns;
-
- base.ApplyToRulesetContainer(rulesetContainer);
- }
-
protected override Score CreateReplayScore(Beatmap beatmap) => new Score
{
User = new User { Username = "osu!topus!" },
- Replay = new ManiaAutoGenerator(beatmap, availableColumns).Generate(),
+ Replay = new ManiaAutoGenerator(beatmap).Generate(),
};
}
}
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
index 64982532a7..153fee3ab6 100644
--- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs
@@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
+using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
@@ -13,15 +13,11 @@ namespace osu.Game.Rulesets.Mania.Replays
{
internal class ManiaAutoGenerator : AutoGenerator
{
- private const double release_delay = 20;
+ public const double RELEASE_DELAY = 20;
- private readonly int availableColumns;
-
- public ManiaAutoGenerator(Beatmap beatmap, int availableColumns)
+ public ManiaAutoGenerator(Beatmap beatmap)
: base(beatmap)
{
- this.availableColumns = availableColumns;
-
Replay = new Replay { User = new User { Username = @"Autoplay" } };
}
@@ -30,104 +26,52 @@ namespace osu.Game.Rulesets.Mania.Replays
public override Replay Generate()
{
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
- Replay.Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None));
+ Replay.Frames.Add(new ManiaReplayFrame(-100000, 0));
- double[] holdEndTimes = new double[availableColumns];
- for (int i = 0; i < availableColumns; i++)
- holdEndTimes[i] = double.NegativeInfinity;
+ var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
- // Notes are handled row-by-row
- foreach (var objGroup in Beatmap.HitObjects.GroupBy(h => h.StartTime))
+ int activeColumns = 0;
+ foreach (var group in pointGroups)
{
- double groupTime = objGroup.Key;
-
- int activeColumns = 0;
-
- // Get the previously held-down active columns
- for (int i = 0; i < availableColumns; i++)
+ foreach (var point in group)
{
- if (holdEndTimes[i] > groupTime)
- activeColumns |= 1 << i;
+ if (point is HitPoint)
+ activeColumns |= 1 << point.Column;
+ if (point is ReleasePoint)
+ activeColumns ^= 1 << point.Column;
}
- // Add on the group columns, keeping track of the held notes for the next rows
- foreach (var obj in objGroup)
- {
- var holdNote = obj as HoldNote;
- if (holdNote != null)
- holdEndTimes[obj.Column] = Math.Max(holdEndTimes[obj.Column], holdNote.EndTime);
-
- activeColumns |= 1 << obj.Column;
- }
-
- Replay.Frames.Add(new ReplayFrame(groupTime, activeColumns, null, ReplayButtonState.None));
-
- // Add the release frames. We can't do this with the loop above because we need activeColumns to be fully populated
- foreach (var obj in objGroup.GroupBy(h => (h as IHasEndTime)?.EndTime ?? h.StartTime + release_delay).OrderBy(h => h.Key))
- {
- var groupEndTime = obj.Key;
-
- int activeColumnsAtEnd = 0;
- for (int i = 0; i < availableColumns; i++)
- {
- if (holdEndTimes[i] > groupEndTime)
- activeColumnsAtEnd |= 1 << i;
- }
-
- Replay.Frames.Add(new ReplayFrame(groupEndTime, activeColumnsAtEnd, 0, ReplayButtonState.None));
- }
+ Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, activeColumns));
}
- Replay.Frames = Replay.Frames
- // Pick the maximum activeColumns for all frames at the same time
- .GroupBy(f => f.Time)
- .Select(g => new ReplayFrame(g.First().Time, maxMouseX(g), 0, ReplayButtonState.None))
- // The addition of release frames above maybe result in unordered frames, but we need them ordered
- .OrderBy(f => f.Time)
- .ToList();
-
return Replay;
}
- ///
- /// Finds the maximum by count of bits from a grouping of s.
- ///
- /// The grouping to search.
- /// The maximum by count of bits.
- private float maxMouseX(IGrouping group)
+ private IEnumerable generateActionPoints()
{
- int currentCount = -1;
- int currentMax = 0;
-
- foreach (var val in group)
+ foreach (var obj in Beatmap.HitObjects)
{
- int newCount = countBits((int)(val.MouseX ?? 0));
- if (newCount > currentCount)
- {
- currentCount = newCount;
- currentMax = (int)(val.MouseX ?? 0);
- }
+ yield return new HitPoint { Time = obj.StartTime, Column = obj.Column };
+ yield return new ReleasePoint { Time = ((obj as IHasEndTime)?.EndTime ?? obj.StartTime) + RELEASE_DELAY, Column = obj.Column };
}
-
- return currentMax;
}
- ///
- /// Counts the number of bits set in a value.
- ///
- /// The value to count.
- /// The number of set bits.
- private int countBits(int value)
+ private interface IActionPoint
{
- int count = 0;
- while (value > 0)
- {
- if ((value & 1) > 0)
- count++;
- value >>= 1;
- }
+ double Time { get; set; }
+ int Column { get; set; }
+ }
- return count;
+ private struct HitPoint : IActionPoint
+ {
+ public double Time { get; set; }
+ public int Column { get; set; }
+ }
+
+ private struct ReleasePoint : IActionPoint
+ {
+ public double Time { get; set; }
+ public int Column { get; set; }
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs
index e352997f2c..12534d6eb4 100644
--- a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs
@@ -2,29 +2,37 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Input;
+using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Mania.Replays
{
internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler
{
- public ManiaFramedReplayInputHandler(Replay replay)
+ private readonly ManiaRulesetContainer container;
+
+ public ManiaFramedReplayInputHandler(Replay replay, ManiaRulesetContainer container)
: base(replay)
{
+ this.container = container;
}
+ private ManiaPlayfield playfield;
public override List GetPendingStates()
{
var actions = new List();
- int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
+ if (playfield == null)
+ playfield = (ManiaPlayfield)container.Playfield;
+ int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
int counter = 0;
while (activeColumns > 0)
{
if ((activeColumns & 1) > 0)
- actions.Add(ManiaAction.Key1 + counter);
+ actions.Add(playfield.Columns.ElementAt(counter).Action);
counter++;
activeColumns >>= 1;
}
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
new file mode 100644
index 0000000000..d1bc7da911
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Replays;
+
+namespace osu.Game.Rulesets.Mania.Replays
+{
+ public class ManiaReplayFrame : ReplayFrame
+ {
+ public override bool IsImportant => MouseX > 0;
+
+ public ManiaReplayFrame(double time, int activeColumns)
+ : base(time, activeColumns, null, ReplayButtonState.None)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs
new file mode 100644
index 0000000000..805553eafc
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs
@@ -0,0 +1,173 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Replays;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Mania.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCaseAutoGeneration : OsuTestCase
+ {
+ [Test]
+ public void TestSingleNote()
+ {
+ // | |
+ // | - |
+ // | |
+
+ var beatmap = new Beatmap();
+ beatmap.HitObjects.Add(new Note { StartTime = 1000 });
+
+ var generated = new ManiaAutoGenerator(beatmap).Generate();
+
+ Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
+ Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
+ Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
+ Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed");
+ Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released");
+ }
+
+ [Test]
+ public void TestSingleHoldNote()
+ {
+ // | |
+ // | * |
+ // | * |
+ // | * |
+ // | |
+
+ var beatmap = new Beatmap();
+ beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
+
+ var generated = new ManiaAutoGenerator(beatmap).Generate();
+
+ Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
+ Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
+ Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed");
+ Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released");
+ }
+
+ [Test]
+ public void TestSingleNoteChord()
+ {
+ // | | |
+ // | - | - |
+ // | | |
+
+ var beatmap = new Beatmap();
+ beatmap.HitObjects.Add(new Note { StartTime = 1000 });
+ beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 });
+
+ var generated = new ManiaAutoGenerator(beatmap).Generate();
+
+ Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
+ Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
+ Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
+ Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed");
+ Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released");
+ }
+
+ [Test]
+ public void TestHoldNoteChord()
+ {
+ // | | |
+ // | * | * |
+ // | * | * |
+ // | * | * |
+ // | | |
+
+ var beatmap = new Beatmap();
+ beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
+ beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 });
+
+ var generated = new ManiaAutoGenerator(beatmap).Generate();
+
+ Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
+ Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
+ Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed");
+ Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released");
+ }
+
+ [Test]
+ public void TestSingleNoteStair()
+ {
+ // | | |
+ // | | - |
+ // | - | |
+ // | | |
+
+ var beatmap = new Beatmap();
+ beatmap.HitObjects.Add(new Note { StartTime = 1000 });
+ beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 });
+
+ var generated = new ManiaAutoGenerator(beatmap).Generate();
+
+ Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
+ Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
+ Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time");
+ Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time");
+ Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
+ Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
+ Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 1 has not been released");
+ Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 2 has not been pressed");
+ Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released");
+ }
+
+ [Test]
+ public void TestHoldNoteStair()
+ {
+ // | | |
+ // | | * |
+ // | * | * |
+ // | * | * |
+ // | * | |
+ // | | |
+
+ var beatmap = new Beatmap();
+ beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
+ beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 });
+
+ var generated = new ManiaAutoGenerator(beatmap).Generate();
+
+ Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
+ Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time");
+ Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time");
+ Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
+ Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
+ Assert.AreEqual(3, generated.Frames[2].MouseX, "Keys 1 and 2 have not been pressed");
+ Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 1 has not been released");
+ Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released");
+ }
+
+ [Test]
+ public void TestHoldNoteWithReleasePress()
+ {
+ // | | |
+ // | * | - |
+ // | * | |
+ // | * | |
+ // | | |
+
+ var beatmap = new Beatmap();
+ beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY });
+ beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
+
+ var generated = new ManiaAutoGenerator(beatmap).Generate();
+
+ Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames");
+ Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
+ Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time");
+ Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time");
+ Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
+ Assert.AreEqual(2, generated.Frames[2].MouseX, "Key 1 has not been released or key 2 has not been pressed");
+ Assert.AreEqual(0, generated.Frames[3].MouseX, "Keys 1 and 2 have not been released");
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs
index 802d4cc99d..aab784f177 100644
--- a/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs
@@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Mania.Tests
private const double start_time = 500;
private const double duration = 500;
- public override string Description => @"Mania playfield";
-
protected override double TimePerAction => 200;
private RulesetInfo maniaRuleset;
diff --git a/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs
new file mode 100644
index 0000000000..8aa8c6b799
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs
@@ -0,0 +1,16 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+
+namespace osu.Game.Rulesets.Mania.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
+ {
+ public TestCasePerformancePoints()
+ : base(new ManiaRuleset(new RulesetInfo()))
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
index 08acd46c57..cbbcb84b31 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
@@ -124,6 +124,6 @@ namespace osu.Game.Rulesets.Mania.UI
protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
- protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
+ protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 112fcb1a30..39ec753fe1 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -45,6 +45,18 @@ namespace osu.Game.Rulesets.Osu.Objects
set { Curve.Distance = value; }
}
+ ///
+ /// The position of the cursor at the point of completion of this if it was hit
+ /// with as few movements as possible. This is set and used by difficulty calculation.
+ ///
+ internal Vector2? LazyEndPosition;
+
+ ///
+ /// The distance travelled by the cursor upon completion of this if it was hit
+ /// with as few movements as possible. This is set and used by difficulty calculation.
+ ///
+ internal float LazyTravelDistance;
+
public List RepeatSamples { get; set; } = new List();
public int RepeatCount { get; set; } = 1;
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs
index 537874f643..3d185ab694 100644
--- a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs
@@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
(h as Slider)?.Curve?.Calculate();
}
- public override double Calculate(Dictionary categoryDifficulty = null)
+ public override double Calculate(Dictionary categoryDifficulty = null)
{
- OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects);
+ OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects, TimeRate);
Skill[] skills =
{
new Aim(),
@@ -67,8 +67,8 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
if (categoryDifficulty != null)
{
- categoryDifficulty.Add("Aim", aimRating.ToString("0.00"));
- categoryDifficulty.Add("Speed", speedRating.ToString("0.00"));
+ categoryDifficulty.Add("Aim", aimRating);
+ categoryDifficulty.Add("Speed", speedRating);
}
return starRating;
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs
index c6ecc3a506..f8e9423e29 100644
--- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs
@@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
/// Creates an enumerator, which preprocesses a list of s recieved as input, wrapping them as
/// which contains extra data required for difficulty calculation.
///
- public OsuDifficultyBeatmap(List objects)
+ public OsuDifficultyBeatmap(List objects, double timeRate)
{
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
// This should probably happen before the objects reach the difficulty calculator.
objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
- difficultyObjects = createDifficultyObjectEnumerator(objects);
+ difficultyObjects = createDifficultyObjectEnumerator(objects, timeRate);
}
///
@@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- private IEnumerator createDifficultyObjectEnumerator(List objects)
+ private IEnumerator createDifficultyObjectEnumerator(List objects, double timeRate)
{
// We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object.
OsuHitObject[] triangle = new OsuHitObject[3];
@@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
triangle[1] = triangle[0];
triangle[0] = objects[i];
- yield return new OsuDifficultyHitObject(triangle);
+ yield return new OsuDifficultyHitObject(triangle, timeRate);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs
index bdeb62df3e..972677a6f1 100644
--- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -2,6 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Linq;
+using OpenTK;
using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
@@ -33,13 +35,17 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
private const int normalized_radius = 52;
+ private readonly double timeRate;
+
private readonly OsuHitObject[] t;
///
/// Initializes the object calculating extra data required for difficulty calculation.
///
- public OsuDifficultyHitObject(OsuHitObject[] triangle)
+ public OsuDifficultyHitObject(OsuHitObject[] triangle, double timeRate)
{
+ this.timeRate = timeRate;
+
t = triangle;
BaseObject = t[0];
setDistances();
@@ -57,14 +63,53 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
scalingFactor *= 1 + smallCircleBonus;
}
- Distance = (t[0].StackedPosition - t[1].StackedPosition).Length * scalingFactor;
+ Vector2 lastCursorPosition = t[1].StackedPosition;
+ float lastTravelDistance = 0;
+
+ var lastSlider = t[1] as Slider;
+ if (lastSlider != null)
+ {
+ computeSliderCursorPosition(lastSlider);
+ lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition;
+ lastTravelDistance = lastSlider.LazyTravelDistance;
+ }
+
+ Distance = (lastTravelDistance + (BaseObject.StackedPosition - lastCursorPosition).Length) * scalingFactor;
}
private void setTimingValues()
{
// Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure.
- DeltaTime = Math.Max(40, t[0].StartTime - t[1].StartTime);
+ DeltaTime = Math.Max(40, (t[0].StartTime - t[1].StartTime) / timeRate);
TimeUntilHit = 450; // BaseObject.PreEmpt;
}
+
+ private void computeSliderCursorPosition(Slider slider)
+ {
+ if (slider.LazyEndPosition != null)
+ return;
+ slider.LazyEndPosition = slider.StackedPosition;
+
+ float approxFollowCircleRadius = (float)(slider.Radius * 3);
+ var computeVertex = new Action(t =>
+ {
+ var diff = slider.PositionAt(t) - slider.LazyEndPosition.Value;
+ float dist = diff.Length;
+
+ if (dist > approxFollowCircleRadius)
+ {
+ // The cursor would be outside the follow circle, we need to move it
+ diff.Normalize(); // Obtain direction of diff
+ dist -= approxFollowCircleRadius;
+ slider.LazyEndPosition += diff * dist;
+ slider.LazyTravelDistance += dist;
+ }
+ });
+
+ var scoringTimes = slider.Ticks.Select(t => t.StartTime).Concat(slider.RepeatPoints.Select(r => r.StartTime)).OrderBy(t => t);
+ foreach (var time in scoringTimes)
+ computeVertex(time);
+ computeVertex(slider.EndTime);
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index c87328d87c..fdf2a458b7 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -14,6 +14,8 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Overlays.Settings;
using osu.Framework.Input.Bindings;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.Osu.Scoring;
namespace osu.Game.Rulesets.Osu
{
@@ -114,6 +116,8 @@ namespace osu.Game.Rulesets.Osu
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods);
+ public override PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score);
+
public override string Description => "osu!";
public override SettingsSubsection CreateSettings() => new OsuSettings();
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs
new file mode 100644
index 0000000000..cd6b6c5e27
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs
@@ -0,0 +1,199 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu.Beatmaps;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Osu.Scoring
+{
+ public class OsuPerformanceCalculator : PerformanceCalculator
+ {
+ private readonly int countHitCircles;
+ private readonly int beatmapMaxCombo;
+
+ private Mod[] mods;
+ private double realApproachRate;
+ private double accuracy;
+ private int scoreMaxCombo;
+ private int count300;
+ private int count100;
+ private int count50;
+ private int countMiss;
+
+ public OsuPerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score)
+ : base(ruleset, beatmap, score)
+ {
+ countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
+
+ beatmapMaxCombo = Beatmap.HitObjects.Count;
+ beatmapMaxCombo += Beatmap.HitObjects.OfType().Sum(s => s.RepeatCount + s.Ticks.Count());
+ }
+
+ public override double Calculate(Dictionary categoryRatings = null)
+ {
+ mods = Score.Mods;
+ accuracy = Score.Accuracy;
+ scoreMaxCombo = Score.MaxCombo;
+ count300 = Convert.ToInt32(Score.Statistics["300"]);
+ count100 = Convert.ToInt32(Score.Statistics["100"]);
+ count50 = Convert.ToInt32(Score.Statistics["50"]);
+ countMiss = Convert.ToInt32(Score.Statistics["x"]);
+
+ // Don't count scores made with supposedly unranked mods
+ if (mods.Any(m => !m.Ranked))
+ return 0;
+
+ // Todo: In the future we should apply changes to PreEmpt/AR at an OsuHitObject/BaseDifficulty level, but this is done
+ // locally for now as doing so would modify animations and other things unexpectedly
+ // DO NOT MODIFY THIS
+ double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate;
+ if (mods.Any(m => m is OsuModHardRock))
+ ar = Math.Min(10, ar * 1.4);
+ if (mods.Any(m => m is OsuModEasy))
+ ar = Math.Max(0, ar / 2);
+ double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450);
+ realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5;
+
+ // Custom multipliers for NoFail and SpunOut.
+ double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
+
+ if (mods.Any(m => m is OsuModNoFail))
+ multiplier *= 0.90f;
+
+ if (mods.Any(m => m is OsuModSpunOut))
+ multiplier *= 0.95f;
+
+ double aimValue = computeAimValue();
+ double speedValue = computeSpeedValue();
+ double accuracyValue = computeAccuracyValue();
+ double totalValue =
+ Math.Pow(
+ Math.Pow(aimValue, 1.1f) +
+ Math.Pow(speedValue, 1.1f) +
+ Math.Pow(accuracyValue, 1.1f), 1.0f / 1.1f
+ ) * multiplier;
+
+ if (categoryRatings != null)
+ {
+ categoryRatings.Add("Aim", aimValue);
+ categoryRatings.Add("Speed", speedValue);
+ categoryRatings.Add("Accuracy", accuracyValue);
+ }
+
+ return totalValue;
+ }
+
+ private double computeAimValue()
+ {
+ double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Aim"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
+
+ // Longer maps are worth more
+ double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
+ (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
+
+ aimValue *= lengthBonus;
+
+ // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
+ aimValue *= Math.Pow(0.97f, countMiss);
+
+ // Combo scaling
+ if (beatmapMaxCombo > 0)
+ aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
+
+ double approachRateFactor = 1.0f;
+ if (realApproachRate > 10.33f)
+ approachRateFactor += 0.45f * (realApproachRate - 10.33f);
+ else if (realApproachRate < 8.0f)
+ {
+ // HD is worth more with lower ar!
+ if (mods.Any(h => h is OsuModHidden))
+ approachRateFactor += 0.02f * (8.0f - realApproachRate);
+ else
+ approachRateFactor += 0.01f * (8.0f - realApproachRate);
+ }
+
+ aimValue *= approachRateFactor;
+
+ if (mods.Any(h => h is OsuModHidden))
+ aimValue *= 1.18f;
+
+ if (mods.Any(h => h is OsuModFlashlight))
+ {
+ // Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
+ aimValue *= 1.45f * lengthBonus;
+ }
+
+ // Scale the aim value with accuracy _slightly_
+ aimValue *= 0.5f + accuracy / 2.0f;
+ // It is important to also consider accuracy difficulty when doing that
+ aimValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500;
+
+ return aimValue;
+ }
+
+ private double computeSpeedValue()
+ {
+ double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Speed"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
+
+ // Longer maps are worth more
+ speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
+ (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
+
+ // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
+ speedValue *= Math.Pow(0.97f, countMiss);
+
+ // Combo scaling
+ if (beatmapMaxCombo > 0)
+ speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
+
+ // Scale the speed value with accuracy _slightly_
+ speedValue *= 0.5f + accuracy / 2.0f;
+ // It is important to also consider accuracy difficulty when doing that
+ speedValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500;
+
+ return speedValue;
+ }
+
+ private double computeAccuracyValue()
+ {
+ // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window
+ double betterAccuracyPercentage;
+ int amountHitObjectsWithAccuracy = countHitCircles;
+
+ if (amountHitObjectsWithAccuracy > 0)
+ betterAccuracyPercentage = ((count300 - (totalHits - amountHitObjectsWithAccuracy)) * 6 + count100 * 2 + count50) / (amountHitObjectsWithAccuracy * 6);
+ else
+ betterAccuracyPercentage = 0;
+
+ // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points
+ if (betterAccuracyPercentage < 0)
+ betterAccuracyPercentage = 0;
+
+ // Lots of arbitrary values from testing.
+ // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
+ double accuracyValue = Math.Pow(1.52163f, Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f;
+
+ // Bonus for many hitcircles - it's harder to keep good accuracy up for longer
+ accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
+
+ if (mods.Any(m => m is OsuModHidden))
+ accuracyValue *= 1.02f;
+ if (mods.Any(m => m is OsuModFlashlight))
+ accuracyValue *= 1.02f;
+
+ return accuracyValue;
+ }
+
+ private double totalHits => count300 + count100 + count50 + countMiss;
+ private double totalSuccessfulHits => count300 + count100 + count50;
+
+ protected override BeatmapConverter CreateBeatmapConverter() => new OsuBeatmapConverter();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs
new file mode 100644
index 0000000000..25a6110459
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs
@@ -0,0 +1,16 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
+ {
+ public TestCasePerformancePoints()
+ : base(new OsuRuleset(new RulesetInfo()))
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs
index e881942fbf..e74c12fa5d 100644
--- a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs
@@ -5,7 +5,6 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects;
using System.Collections.Generic;
-using System.Globalization;
using System;
namespace osu.Game.Rulesets.Taiko
@@ -36,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko
{
}
- public override double Calculate(Dictionary categoryDifficulty = null)
+ public override double Calculate(Dictionary categoryDifficulty = null)
{
// Fill our custom DifficultyHitObject class, that carries additional information
difficultyHitObjects.Clear();
@@ -53,8 +52,8 @@ namespace osu.Game.Rulesets.Taiko
if (categoryDifficulty != null)
{
- categoryDifficulty.Add("Strain", starRating.ToString("0.00", CultureInfo.InvariantCulture));
- categoryDifficulty.Add("Hit window 300", (35 /*HitObjectManager.HitWindow300*/ / TimeRate).ToString("0.00", CultureInfo.InvariantCulture));
+ categoryDifficulty.Add("Strain", starRating);
+ categoryDifficulty.Add("Hit window 300", 35 /*HitObjectManager.HitWindow300*/ / TimeRate);
}
return starRating;
diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs
new file mode 100644
index 0000000000..96d5b20b6e
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs
@@ -0,0 +1,16 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+
+namespace osu.Game.Rulesets.Taiko.Tests
+{
+ [Ignore("getting CI working")]
+ public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
+ {
+ public TestCasePerformancePoints()
+ : base(new TaikoRuleset(new RulesetInfo()))
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs
index 059d297401..555f9bb0da 100644
--- a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs
@@ -30,8 +30,6 @@ namespace osu.Game.Rulesets.Taiko.Tests
private const double default_duration = 1000;
private const float scroll_time = 1000;
- public override string Description => "Taiko playfield";
-
protected override double TimePerAction => default_duration * 2;
private readonly Random rng = new Random(1337);
diff --git a/osu.Game.Tests/Visual/TestCaseAllPlayers.cs b/osu.Game.Tests/Visual/TestCaseAllPlayers.cs
index 8c63e1a274..62b99487a9 100644
--- a/osu.Game.Tests/Visual/TestCaseAllPlayers.cs
+++ b/osu.Game.Tests/Visual/TestCaseAllPlayers.cs
@@ -5,6 +5,5 @@ namespace osu.Game.Tests.Visual
{
public class TestCaseAllPlayers : TestCasePlayer
{
- public override string Description => @"Showing everything to play the game.";
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs
index a5156c155b..18555574ba 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs
@@ -19,8 +19,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseBeatSyncedContainer : OsuTestCase
{
- public override string Description => @"Tests beat synced containers.";
-
private readonly MusicController mc;
public TestCaseBeatSyncedContainer()
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs b/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs
index 7dffa6d590..1a64994d0e 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapDetailArea.cs
@@ -9,10 +9,9 @@ using OpenTK;
namespace osu.Game.Tests.Visual
{
[TestFixture]
+ [System.ComponentModel.Description("PlaySongSelect leaderboard/details area")]
internal class TestCaseBeatmapDetailArea : OsuTestCase
{
- public override string Description => @"Beatmap details in song select";
-
public TestCaseBeatmapDetailArea()
{
Add(new BeatmapDetailArea
@@ -23,4 +22,4 @@ namespace osu.Game.Tests.Visual
});
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs b/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs
index b31eded9a0..248ec6d43d 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapDetails.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.ComponentModel;
using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
@@ -8,10 +9,9 @@ using osu.Game.Screens.Select;
namespace osu.Game.Tests.Visual
{
+ [Description("PlaySongSelect beatmap details")]
internal class TestCaseBeatmapDetails : OsuTestCase
{
- public override string Description => "BeatmapDetails tab of BeatmapDetailArea";
-
public TestCaseBeatmapDetails()
{
BeatmapDetails details;
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs b/osu.Game.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs
index bdc2e0e105..e114fac96e 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapOptionsOverlay.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.ComponentModel;
using osu.Game.Graphics;
using osu.Game.Screens.Select.Options;
using OpenTK.Graphics;
@@ -8,10 +9,9 @@ using OpenTK.Input;
namespace osu.Game.Tests.Visual
{
+ [Description("bottom beatmap details")]
internal class TestCaseBeatmapOptionsOverlay : OsuTestCase
{
- public override string Description => @"Beatmap options in song select";
-
public TestCaseBeatmapOptionsOverlay()
{
var overlay = new BeatmapOptionsOverlay();
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
index 8cae3feae2..cef8797f20 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
@@ -3,7 +3,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
@@ -14,13 +13,13 @@ using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Users;
using System.Collections.Generic;
+using osu.Framework.Graphics.Containers;
namespace osu.Game.Tests.Visual
{
+ [System.ComponentModel.Description("in BeatmapOverlay")]
public class TestCaseBeatmapScoresContainer : OsuTestCase
{
- public override string Description => "BeatmapOverlay scores container";
-
private readonly IEnumerable scores;
private readonly IEnumerable anotherScores;
private readonly OnlineScore topScore;
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs
index c2de4ec05d..c24e13b7fb 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs
@@ -14,8 +14,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseBeatmapSetOverlay : OsuTestCase
{
- public override string Description => @"view online beatmap sets";
-
private readonly BeatmapSetOverlay overlay;
public TestCaseBeatmapSetOverlay()
diff --git a/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs b/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs
index 97c343f8ac..50abd11e79 100644
--- a/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs
+++ b/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs
@@ -8,8 +8,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseBreadcrumbs : OsuTestCase
{
- public override string Description => @"breadcrumb > control";
-
public TestCaseBreadcrumbs()
{
BreadcrumbControl c;
diff --git a/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs b/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs
index 206ca308cf..dcb3b74654 100644
--- a/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs
@@ -10,8 +10,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseBreakOverlay : OsuTestCase
{
- public override string Description => @"Tests breaks behavior";
-
private readonly BreakOverlay breakOverlay;
public TestCaseBreakOverlay()
@@ -88,4 +86,4 @@ namespace osu.Game.Tests.Visual
};
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Tests/Visual/TestCaseButtonSystem.cs b/osu.Game.Tests/Visual/TestCaseButtonSystem.cs
new file mode 100644
index 0000000000..d260de69f1
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseButtonSystem.cs
@@ -0,0 +1,33 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Screens.Menu;
+using OpenTK.Graphics;
+
+namespace osu.Game.Tests.Visual
+{
+ internal class TestCaseButtonSystem : OsuTestCase
+ {
+ public TestCaseButtonSystem()
+ {
+ OsuLogo logo;
+ ButtonSystem buttons;
+
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke),
+ RelativeSizeAxes = Axes.Both,
+ },
+ buttons = new ButtonSystem(),
+ logo = new OsuLogo()
+ };
+
+ buttons.SetOsuLogo(logo);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs
index 847593fcec..85ee224a5e 100644
--- a/osu.Game.Tests/Visual/TestCaseChatDisplay.cs
+++ b/osu.Game.Tests/Visual/TestCaseChatDisplay.cs
@@ -1,15 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.ComponentModel;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual
{
+ [Description("Testing chat api and overlay")]
internal class TestCaseChatDisplay : OsuTestCase
{
- public override string Description => @"Testing chat api and overlay";
-
public TestCaseChatDisplay()
{
Add(new ChatOverlay
diff --git a/osu.Game.Tests/Visual/TestCaseContextMenu.cs b/osu.Game.Tests/Visual/TestCaseContextMenu.cs
index 91a766f8c7..6f5cb398d7 100644
--- a/osu.Game.Tests/Visual/TestCaseContextMenu.cs
+++ b/osu.Game.Tests/Visual/TestCaseContextMenu.cs
@@ -15,8 +15,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseContextMenu : OsuTestCase
{
- public override string Description => @"Menu visible on right click";
-
private const int start_time = 0;
private const int duration = 1000;
diff --git a/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs b/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs
index a031125b3a..f1aba908f0 100644
--- a/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs
@@ -9,8 +9,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseDialogOverlay : OsuTestCase
{
- public override string Description => @"Display dialogs";
-
public TestCaseDialogOverlay()
{
DialogOverlay overlay;
diff --git a/osu.Game.Tests/Visual/TestCaseDirect.cs b/osu.Game.Tests/Visual/TestCaseDirect.cs
index 2d8677c391..ede17fa775 100644
--- a/osu.Game.Tests/Visual/TestCaseDirect.cs
+++ b/osu.Game.Tests/Visual/TestCaseDirect.cs
@@ -11,8 +11,6 @@ namespace osu.Game.Tests.Visual
{
public class TestCaseDirect : OsuTestCase
{
- public override string Description => @"osu!direct overlay";
-
private DirectOverlay direct;
private RulesetStore rulesets;
diff --git a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs
index 7113bcbff5..a51cf8ca95 100644
--- a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs
+++ b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs
@@ -14,8 +14,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseDrawableRoom : OsuTestCase
{
- public override string Description => @"Select your favourite room";
-
private RulesetStore rulesets;
protected override void LoadComplete()
diff --git a/osu.Game.Tests/Visual/TestCaseDrawings.cs b/osu.Game.Tests/Visual/TestCaseDrawings.cs
index c805d88cb4..e5692b29de 100644
--- a/osu.Game.Tests/Visual/TestCaseDrawings.cs
+++ b/osu.Game.Tests/Visual/TestCaseDrawings.cs
@@ -2,15 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
+using System.ComponentModel;
using osu.Game.Screens.Tournament;
using osu.Game.Screens.Tournament.Teams;
namespace osu.Game.Tests.Visual
{
+ [Description("for tournament use")]
internal class TestCaseDrawings : OsuTestCase
{
- public override string Description => "Tournament drawings";
-
public TestCaseDrawings()
{
Add(new Drawings
diff --git a/osu.Game.Tests/Visual/TestCaseGamefield.cs b/osu.Game.Tests/Visual/TestCaseGamefield.cs
index af86b6ec06..0d8f4cb5f7 100644
--- a/osu.Game.Tests/Visual/TestCaseGamefield.cs
+++ b/osu.Game.Tests/Visual/TestCaseGamefield.cs
@@ -7,8 +7,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseGamefield : OsuTestCase
{
- public override string Description => @"Showing hitobjects and what not.";
-
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game.Tests/Visual/TestCaseGraph.cs b/osu.Game.Tests/Visual/TestCaseGraph.cs
index 714f284879..fb1a3ef3f6 100644
--- a/osu.Game.Tests/Visual/TestCaseGraph.cs
+++ b/osu.Game.Tests/Visual/TestCaseGraph.cs
@@ -10,8 +10,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseGraph : OsuTestCase
{
- public override string Description => "graph";
-
public TestCaseGraph()
{
BarGraph graph;
@@ -36,4 +34,4 @@ namespace osu.Game.Tests.Visual
AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft);
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game.Tests/Visual/TestCaseIconButton.cs b/osu.Game.Tests/Visual/TestCaseIconButton.cs
index acde9df4a9..345316708e 100644
--- a/osu.Game.Tests/Visual/TestCaseIconButton.cs
+++ b/osu.Game.Tests/Visual/TestCaseIconButton.cs
@@ -14,8 +14,6 @@ namespace osu.Game.Tests.Visual
{
public class TestCaseIconButton : OsuTestCase
{
- public override string Description => "Various display modes of icon buttons";
-
public TestCaseIconButton()
{
Child = new FillFlowContainer
diff --git a/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs b/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs
index e473ce8778..d39ac12a60 100644
--- a/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs
+++ b/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs
@@ -9,8 +9,6 @@ namespace osu.Game.Tests.Visual
{
private readonly KeyBindingOverlay overlay;
- public override string Description => @"Key configuration";
-
public TestCaseKeyConfiguration()
{
Child = overlay = new KeyBindingOverlay();
diff --git a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
index 622fb15f4f..df122b7132 100644
--- a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
+++ b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
@@ -10,8 +10,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseKeyCounter : OsuTestCase
{
- public override string Description => @"Tests key counter";
-
public TestCaseKeyCounter()
{
KeyCounterCollection kc = new KeyCounterCollection
diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs
index 832003e6ab..9d6fb3a4ec 100644
--- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs
+++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.ComponentModel;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards;
@@ -9,10 +10,9 @@ using OpenTK;
namespace osu.Game.Tests.Visual
{
+ [Description("PlaySongSelect leaderboard")]
internal class TestCaseLeaderboard : OsuTestCase
{
- public override string Description => @"From song select";
-
private readonly Leaderboard leaderboard;
private void newScores()
diff --git a/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs b/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs
index 9a26eefd63..fbee27668c 100644
--- a/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs
@@ -11,8 +11,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseMedalOverlay : OsuTestCase
{
- public override string Description => @"medal get!";
-
public override IReadOnlyList RequiredTypes => new[]
{
typeof(MedalOverlay),
diff --git a/osu.Game.Tests/Visual/TestCaseMenuButtonSystem.cs b/osu.Game.Tests/Visual/TestCaseMenuButtonSystem.cs
deleted file mode 100644
index 0b50c9cf47..0000000000
--- a/osu.Game.Tests/Visual/TestCaseMenuButtonSystem.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Graphics.Colour;
-using osu.Framework.Graphics.Shapes;
-using osu.Game.Screens.Menu;
-using OpenTK.Graphics;
-
-namespace osu.Game.Tests.Visual
-{
- internal class TestCaseMenuButtonSystem : OsuTestCase
- {
- public override string Description => @"Main menu button system";
-
- public TestCaseMenuButtonSystem()
- {
- Add(new Box
- {
- Colour = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke),
- RelativeSizeAxes = Framework.Graphics.Axes.Both,
- });
- Add(new ButtonSystem());
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/TestCaseMenuOverlays.cs b/osu.Game.Tests/Visual/TestCaseMenuOverlays.cs
index e27de96bee..94a69f0029 100644
--- a/osu.Game.Tests/Visual/TestCaseMenuOverlays.cs
+++ b/osu.Game.Tests/Visual/TestCaseMenuOverlays.cs
@@ -1,16 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.ComponentModel;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
+ [Description("player pause/fail screens")]
internal class TestCaseMenuOverlays : OsuTestCase
{
- public override string Description => @"Tests pause and fail overlays";
-
public TestCaseMenuOverlays()
{
FailOverlay failOverlay;
diff --git a/osu.Game.Tests/Visual/TestCaseMods.cs b/osu.Game.Tests/Visual/TestCaseMods.cs
index 0447d6582d..5270ac0dc9 100644
--- a/osu.Game.Tests/Visual/TestCaseMods.cs
+++ b/osu.Game.Tests/Visual/TestCaseMods.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays.Mods;
@@ -10,10 +11,9 @@ using OpenTK;
namespace osu.Game.Tests.Visual
{
+ [Description("mod select and icon display")]
internal class TestCaseMods : OsuTestCase
{
- public override string Description => @"Mod select overlay and in-game display";
-
private ModSelectOverlay modSelect;
private ModDisplay modDisplay;
diff --git a/osu.Game.Tests/Visual/TestCaseMusicController.cs b/osu.Game.Tests/Visual/TestCaseMusicController.cs
index 323c32bf10..3c544bb968 100644
--- a/osu.Game.Tests/Visual/TestCaseMusicController.cs
+++ b/osu.Game.Tests/Visual/TestCaseMusicController.cs
@@ -13,8 +13,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseMusicController : OsuTestCase
{
- public override string Description => @"Tests music controller ui.";
-
private readonly Bindable beatmapBacking = new Bindable();
public TestCaseMusicController()
diff --git a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
index ed331076b2..3dca860909 100644
--- a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs
@@ -15,8 +15,6 @@ namespace osu.Game.Tests.Visual
[TestFixture]
internal class TestCaseNotificationOverlay : OsuTestCase
{
- public override string Description => @"I handle notifications";
-
private readonly NotificationOverlay manager;
public TestCaseNotificationOverlay()
diff --git a/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs b/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs
index 8749d240f8..c3a755f3ca 100644
--- a/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs
+++ b/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs
@@ -12,8 +12,6 @@ namespace osu.Game.Tests.Visual
private FrameworkConfigManager config;
private Bindable frameSyncMode;
- public override string Description => @"Make it easier to see setting changes";
-
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
index 37dd60a25c..7c070fd3df 100644
--- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
@@ -22,8 +22,6 @@ namespace osu.Game.Tests.Visual
{
private BeatmapManager manager;
- public override string Description => @"with fake data";
-
private RulesetStore rulesets;
private DependencyContainer dependencies;
diff --git a/osu.Game.Tests/Visual/TestCaseReplay.cs b/osu.Game.Tests/Visual/TestCaseReplay.cs
index 2e56daccfc..62c8a64916 100644
--- a/osu.Game.Tests/Visual/TestCaseReplay.cs
+++ b/osu.Game.Tests/Visual/TestCaseReplay.cs
@@ -10,8 +10,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseReplay : TestCasePlayer
{
- public override string Description => @"Testing replay playback.";
-
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
diff --git a/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs b/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs
index 3105a7d588..badb98e6b7 100644
--- a/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs
@@ -10,8 +10,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseReplaySettingsOverlay : OsuTestCase
{
- public override string Description => @"Settings visible in replay/auto";
-
public TestCaseReplaySettingsOverlay()
{
ExampleContainer container;
@@ -24,7 +22,7 @@ namespace osu.Game.Tests.Visual
Add(container = new ExampleContainer());
- AddStep(@"Add button", () => container.Add(new OsuButton
+ AddStep(@"Add button", () => container.Add(new TriangleButton
{
RelativeSizeAxes = Axes.X,
Text = @"Button",
diff --git a/osu.Game.Tests/Visual/TestCaseResults.cs b/osu.Game.Tests/Visual/TestCaseResults.cs
index 62154a535a..f1bbb8fed6 100644
--- a/osu.Game.Tests/Visual/TestCaseResults.cs
+++ b/osu.Game.Tests/Visual/TestCaseResults.cs
@@ -15,8 +15,6 @@ namespace osu.Game.Tests.Visual
{
private BeatmapManager beatmaps;
- public override string Description => @"Results after playing.";
-
[BackgroundDependencyLoader]
private void load(BeatmapManager beatmaps)
{
diff --git a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs
index e6b57c970b..51b6ae8e50 100644
--- a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs
+++ b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs
@@ -13,8 +13,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseRoomInspector : OsuTestCase
{
- public override string Description => @"from the multiplayer lobby";
-
private RulesetStore rulesets;
protected override void LoadComplete()
diff --git a/osu.Game.Tests/Visual/TestCaseScoreCounter.cs b/osu.Game.Tests/Visual/TestCaseScoreCounter.cs
index 543ff12fcb..5a04000900 100644
--- a/osu.Game.Tests/Visual/TestCaseScoreCounter.cs
+++ b/osu.Game.Tests/Visual/TestCaseScoreCounter.cs
@@ -12,8 +12,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseScoreCounter : OsuTestCase
{
- public override string Description => @"Tests multiple counters";
-
public TestCaseScoreCounter()
{
int numerator = 0, denominator = 0;
diff --git a/osu.Game.Tests/Visual/TestCaseSettings.cs b/osu.Game.Tests/Visual/TestCaseSettings.cs
index dfc0b66e21..63d798cd53 100644
--- a/osu.Game.Tests/Visual/TestCaseSettings.cs
+++ b/osu.Game.Tests/Visual/TestCaseSettings.cs
@@ -7,8 +7,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseSettings : OsuTestCase
{
- public override string Description => @"Tests the settings overlay";
-
private readonly SettingsOverlay settings;
public TestCaseSettings()
diff --git a/osu.Game.Tests/Visual/TestCaseSkipButton.cs b/osu.Game.Tests/Visual/TestCaseSkipButton.cs
index 49be015adf..40c8baaac8 100644
--- a/osu.Game.Tests/Visual/TestCaseSkipButton.cs
+++ b/osu.Game.Tests/Visual/TestCaseSkipButton.cs
@@ -7,8 +7,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseSkipButton : OsuTestCase
{
- public override string Description => @"Skip skip skippediskip";
-
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game.Tests/Visual/TestCaseSocial.cs b/osu.Game.Tests/Visual/TestCaseSocial.cs
index 4f90e4ceff..ff0707c8ab 100644
--- a/osu.Game.Tests/Visual/TestCaseSocial.cs
+++ b/osu.Game.Tests/Visual/TestCaseSocial.cs
@@ -8,8 +8,6 @@ namespace osu.Game.Tests.Visual
{
public class TestCaseSocial : OsuTestCase
{
- public override string Description => @"social browser overlay";
-
public TestCaseSocial()
{
SocialOverlay s = new SocialOverlay
diff --git a/osu.Game.Tests/Visual/TestCaseSongProgress.cs b/osu.Game.Tests/Visual/TestCaseSongProgress.cs
index 96ff76e9c6..1e6886cda9 100644
--- a/osu.Game.Tests/Visual/TestCaseSongProgress.cs
+++ b/osu.Game.Tests/Visual/TestCaseSongProgress.cs
@@ -12,8 +12,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseSongProgress : OsuTestCase
{
- public override string Description => @"With fake data";
-
private readonly SongProgress progress;
private readonly SongProgressGraph graph;
diff --git a/osu.Game.Tests/Visual/TestCaseStoryboard.cs b/osu.Game.Tests/Visual/TestCaseStoryboard.cs
index c6ef3f4ecf..1dad106cbe 100644
--- a/osu.Game.Tests/Visual/TestCaseStoryboard.cs
+++ b/osu.Game.Tests/Visual/TestCaseStoryboard.cs
@@ -16,8 +16,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseStoryboard : OsuTestCase
{
- public override string Description => @"Tests storyboards.";
-
private readonly Bindable beatmapBacking = new Bindable();
private readonly Container storyboardContainer;
diff --git a/osu.Game.Tests/Visual/TestCaseTabControl.cs b/osu.Game.Tests/Visual/TestCaseTabControl.cs
index 6db74f2cfb..44a1732e16 100644
--- a/osu.Game.Tests/Visual/TestCaseTabControl.cs
+++ b/osu.Game.Tests/Visual/TestCaseTabControl.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.ComponentModel;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
@@ -9,10 +10,9 @@ using OpenTK;
namespace osu.Game.Tests.Visual
{
+ [Description("SongSelect filter control")]
public class TestCaseTabControl : OsuTestCase
{
- public override string Description => @"Filter for song select";
-
public TestCaseTabControl()
{
OsuSpriteText text;
diff --git a/osu.Game.Tests/Visual/TestCaseTextAwesome.cs b/osu.Game.Tests/Visual/TestCaseTextAwesome.cs
index beec5ab271..37905a1883 100644
--- a/osu.Game.Tests/Visual/TestCaseTextAwesome.cs
+++ b/osu.Game.Tests/Visual/TestCaseTextAwesome.cs
@@ -12,8 +12,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseTextAwesome : OsuTestCase
{
- public override string Description => @"Tests display of icons";
-
public TestCaseTextAwesome()
{
FillFlowContainer flow;
diff --git a/osu.Game.Tests/Visual/TestCaseTwoLayerButton.cs b/osu.Game.Tests/Visual/TestCaseTwoLayerButton.cs
index 83e0e4b442..bd5c10d147 100644
--- a/osu.Game.Tests/Visual/TestCaseTwoLayerButton.cs
+++ b/osu.Game.Tests/Visual/TestCaseTwoLayerButton.cs
@@ -1,14 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.ComponentModel;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual
{
+ [Description("mostly back button")]
internal class TestCaseTwoLayerButton : OsuTestCase
{
- public override string Description => @"Mostly back button";
-
public TestCaseTwoLayerButton()
{
Add(new BackButton());
diff --git a/osu.Game.Tests/Visual/TestCaseUserPanel.cs b/osu.Game.Tests/Visual/TestCaseUserPanel.cs
index 8523a754f8..8d94a0c90f 100644
--- a/osu.Game.Tests/Visual/TestCaseUserPanel.cs
+++ b/osu.Game.Tests/Visual/TestCaseUserPanel.cs
@@ -10,8 +10,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseUserPanel : OsuTestCase
{
- public override string Description => @"Panels for displaying a user's status";
-
public TestCaseUserPanel()
{
UserPanel flyte;
diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
index f5fced2915..90daf1e996 100644
--- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs
+++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
@@ -10,8 +10,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseUserProfile : OsuTestCase
{
- public override string Description => "Tests user's profile page.";
-
public TestCaseUserProfile()
{
var profile = new UserProfileOverlay();
diff --git a/osu.Game.Tests/Visual/TestCaseUserRanks.cs b/osu.Game.Tests/Visual/TestCaseUserRanks.cs
index e17f0e1a46..eb0678203c 100644
--- a/osu.Game.Tests/Visual/TestCaseUserRanks.cs
+++ b/osu.Game.Tests/Visual/TestCaseUserRanks.cs
@@ -15,8 +15,6 @@ namespace osu.Game.Tests.Visual
{
internal class TestCaseUserRanks : OsuTestCase
{
- public override string Description => "showing your latest achievements";
-
public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableScore), typeof(RanksSection) };
public TestCaseUserRanks()
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index 962c790fb2..e087eebbfe 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -80,6 +80,7 @@ namespace osu.Game.Beatmaps
///
/// Performs the conversion of a hit object.
+ /// This method is generally executed sequentially for all objects in a beatmap.
///
/// The hit object to convert.
/// The un-converted Beatmap.
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index ad28bf745c..5bdff54b20 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -17,6 +17,7 @@ using osu.Framework.Platform;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
using osu.Game.Database;
+using osu.Game.Graphics;
using osu.Game.IO;
using osu.Game.IPC;
using osu.Game.Online.API;
@@ -52,6 +53,11 @@ namespace osu.Game.Beatmaps
///
public event Action BeatmapRestored;
+ ///
+ /// Fired when a beatmap download begins.
+ ///
+ public event Action BeatmapDownloadBegan;
+
///
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
///
@@ -221,21 +227,29 @@ namespace osu.Game.Beatmaps
/// Downloads a beatmap.
///
/// The to be downloaded.
- /// A new , or an existing one if a download is already in progress.
- public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo)
+ /// Whether the beatmap should be downloaded without video. Defaults to false.
+ public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
{
var existing = GetExistingDownload(beatmapSetInfo);
- if (existing != null) return existing;
+ if (existing != null || api == null) return;
- if (api == null) return null;
+ if (!api.LocalUser.Value.IsSupporter)
+ {
+ PostNotification?.Invoke(new SimpleNotification
+ {
+ Icon = FontAwesome.fa_superpowers,
+ Text = "You gotta be a supporter to download for now 'yo"
+ });
+ return;
+ }
ProgressNotification downloadNotification = new ProgressNotification
{
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
};
- var request = new DownloadBeatmapSetRequest(beatmapSetInfo);
+ var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);
request.DownloadProgressed += progress =>
{
@@ -280,8 +294,7 @@ namespace osu.Game.Beatmaps
// don't run in the main api queue as this is a long-running task.
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
-
- return request;
+ BeatmapDownloadBegan?.Invoke(request);
}
///
diff --git a/osu.Game/Beatmaps/BeatmapMetrics.cs b/osu.Game/Beatmaps/BeatmapMetrics.cs
index 730cf635da..e0cd5f10e7 100644
--- a/osu.Game/Beatmaps/BeatmapMetrics.cs
+++ b/osu.Game/Beatmaps/BeatmapMetrics.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps
public class BeatmapMetrics
{
///
- /// Total vote counts of user ratings on a scale of 0..length.
+ /// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?).
///
public IEnumerable Ratings { get; set; }
diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs
index f58f433cb2..687e1b2177 100644
--- a/osu.Game/Beatmaps/DifficultyCalculator.cs
+++ b/osu.Game/Beatmaps/DifficultyCalculator.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps
{
protected double TimeRate = 1;
- public abstract double Calculate(Dictionary categoryDifficulty = null);
+ public abstract double Calculate(Dictionary categoryDifficulty = null);
}
public abstract class DifficultyCalculator : DifficultyCalculator where T : HitObject
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs
index 917376969b..85daefea57 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs
@@ -64,11 +64,7 @@ namespace osu.Game.Beatmaps.Drawables
{
RelativeSizeAxes = Axes.Both,
OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
- }
- )
- {
- TimeBeforeLoad = 300,
- },
+ }, 300),
new FillFlowContainer
{
Direction = FillDirection.Vertical,
diff --git a/osu.Game/Beatmaps/Drawables/Panel.cs b/osu.Game/Beatmaps/Drawables/Panel.cs
index d6ed306b39..c990a0ea46 100644
--- a/osu.Game/Beatmaps/Drawables/Panel.cs
+++ b/osu.Game/Beatmaps/Drawables/Panel.cs
@@ -3,12 +3,18 @@
using System;
using osu.Framework;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.MathUtils;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
@@ -22,6 +28,10 @@ namespace osu.Game.Beatmaps.Drawables
private readonly Container nestedContainer;
+ private readonly Container borderContainer;
+
+ private readonly Box hoverLayer;
+
protected override Container Content => nestedContainer;
protected Panel()
@@ -29,20 +39,56 @@ namespace osu.Game.Beatmaps.Drawables
Height = MAX_HEIGHT;
RelativeSizeAxes = Axes.X;
- AddInternal(nestedContainer = new Container
+ AddInternal(borderContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 10,
BorderColour = new Color4(221, 255, 255, 255),
+ Children = new Drawable[]
+ {
+ nestedContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ hoverLayer = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ Blending = BlendingMode.Additive,
+ },
+ }
});
Alpha = 0;
}
+ private SampleChannel sampleHover;
+
+ [BackgroundDependencyLoader]
+ private void load(AudioManager audio, OsuColour colours)
+ {
+ sampleHover = audio.Sample.Get($@"SongSelect/song-ping-variation-{RNG.Next(1, 5)}");
+ hoverLayer.Colour = colours.Blue.Opacity(0.1f);
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ sampleHover?.Play();
+
+ hoverLayer.FadeIn(100, Easing.OutQuint);
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ hoverLayer.FadeOut(1000, Easing.OutQuint);
+ base.OnHoverLost(state);
+ }
+
public void SetMultiplicativeAlpha(float alpha)
{
- nestedContainer.Alpha = alpha;
+ borderContainer.Alpha = alpha;
}
protected override void LoadComplete()
@@ -94,8 +140,8 @@ namespace osu.Game.Beatmaps.Drawables
protected virtual void Selected()
{
- nestedContainer.BorderThickness = 2.5f;
- nestedContainer.EdgeEffect = new EdgeEffectParameters
+ borderContainer.BorderThickness = 2.5f;
+ borderContainer.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = new Color4(130, 204, 255, 150),
@@ -106,8 +152,8 @@ namespace osu.Game.Beatmaps.Drawables
protected virtual void Deselected()
{
- nestedContainer.BorderThickness = 0;
- nestedContainer.EdgeEffect = new EdgeEffectParameters
+ borderContainer.BorderThickness = 0;
+ borderContainer.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(1),
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index b3e99fce06..1a7d29e907 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -16,12 +16,13 @@ namespace osu.Game.Configuration
Set(OsuSetting.Ruleset, 0, 0, int.MaxValue);
Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details);
+ Set(OsuSetting.ShowConvertedBeatmaps, true);
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1);
Set(OsuSetting.SelectionRandomType, SelectionRandomType.RandomPermutation);
- Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1, 0.01);
+ Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1);
// Online settings
Set(OsuSetting.Username, string.Empty);
@@ -112,6 +113,7 @@ namespace osu.Game.Configuration
SnakingOutSliders,
ShowFpsDisplay,
ChatDisplayHeight,
- Version
+ Version,
+ ShowConvertedBeatmaps
}
}
diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs
index bc1b7132eb..d8c3ce6694 100644
--- a/osu.Game/Database/DatabaseBackedStore.cs
+++ b/osu.Game/Database/DatabaseBackedStore.cs
@@ -35,6 +35,7 @@ namespace osu.Game.Database
var id = obj.ID;
obj = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find(id);
+ context.Entry(obj).Reload();
}
///
diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs
index 11c049ed3e..8df533ad6e 100644
--- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs
@@ -2,34 +2,39 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
-using osu.Framework.Audio;
-using osu.Framework.Audio.Sample;
+using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
+using osu.Game.Graphics.UserInterface;
namespace osu.Game.Graphics.Containers
{
public class OsuClickableContainer : ClickableContainer
{
- protected SampleChannel SampleClick, SampleHover;
+ private readonly HoverSampleSet sampleSet;
+
+ private readonly Container content = new Container { RelativeSizeAxes = Axes.Both };
+
+ protected override Container Content => content;
+
+ public OsuClickableContainer(HoverSampleSet sampleSet = HoverSampleSet.Normal)
+ {
+ this.sampleSet = sampleSet;
+ }
[BackgroundDependencyLoader]
- private void load(AudioManager audio)
+ private void load()
{
- SampleHover = audio.Sample.Get(@"UI/generic-hover");
- SampleClick = audio.Sample.Get(@"UI/generic-click");
- }
+ if (AutoSizeAxes != Axes.None)
+ {
+ content.RelativeSizeAxes = RelativeSizeAxes;
+ content.AutoSizeAxes = AutoSizeAxes;
+ }
- protected override bool OnHover(InputState state)
- {
- SampleHover?.Play();
- return base.OnHover(state);
- }
-
- protected override bool OnClick(InputState state)
- {
- SampleClick?.Play();
- return base.OnClick(state);
+ InternalChildren = new Drawable[]
+ {
+ content,
+ new HoverClickSounds(sampleSet)
+ };
}
}
}
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index 4ea4f4cdc3..c788df3066 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -16,8 +16,8 @@ namespace osu.Game.Graphics.Containers
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
- samplePopIn = audio.Sample.Get(@"UI/melodic-5");
- samplePopOut = audio.Sample.Get(@"UI/melodic-4");
+ samplePopIn = audio.Sample.Get(@"UI/overlay-pop-in");
+ samplePopOut = audio.Sample.Get(@"UI/overlay-pop-out");
StateChanged += onStateChanged;
}
diff --git a/osu.Game/Graphics/SpriteIcon.cs b/osu.Game/Graphics/SpriteIcon.cs
index ca108bfa7a..e752b1d91a 100644
--- a/osu.Game/Graphics/SpriteIcon.cs
+++ b/osu.Game/Graphics/SpriteIcon.cs
@@ -57,19 +57,31 @@ namespace osu.Game.Graphics
private void load(FontStore store)
{
this.store = store;
-
updateTexture();
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ updateTexture();
+ }
+
+ private FontAwesome loadedIcon;
private void updateTexture()
{
- var texture = store?.Get(((char)icon).ToString());
+ var loadableIcon = icon;
+
+ if (loadableIcon == loadedIcon) return;
+
+ var texture = store?.Get(((char)loadableIcon).ToString());
spriteMain.Texture = texture;
spriteShadow.Texture = texture;
if (Size == Vector2.Zero)
Size = new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0);
+
+ loadedIcon = loadableIcon;
}
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
diff --git a/osu.Game/Graphics/UserInterface/Bar.cs b/osu.Game/Graphics/UserInterface/Bar.cs
index 20df553142..c25a9bf5e9 100644
--- a/osu.Game/Graphics/UserInterface/Bar.cs
+++ b/osu.Game/Graphics/UserInterface/Bar.cs
@@ -20,6 +20,7 @@ namespace osu.Game.Graphics.UserInterface
private const Easing easing = Easing.InOutCubic;
private float length;
+
///
/// Length of the bar, ranges from 0 to 1
///
@@ -134,4 +135,4 @@ namespace osu.Game.Graphics.UserInterface
Vertical = TopToBottom | BottomToTop,
Horizontal = LeftToRight | RightToLeft,
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
new file mode 100644
index 0000000000..0fac1c8092
--- /dev/null
+++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
@@ -0,0 +1,36 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Extensions;
+using osu.Framework.Input;
+
+namespace osu.Game.Graphics.UserInterface
+{
+ ///
+ /// Adds hover and click sounds to a drawable.
+ /// Does not draw anything.
+ ///
+ public class HoverClickSounds : HoverSounds
+ {
+ private SampleChannel sampleClick;
+
+ public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) : base(sampleSet)
+ {
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ sampleClick?.Play();
+ return base.OnClick(state);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(AudioManager audio)
+ {
+ sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}");
+ }
+ }
+}
diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs
new file mode 100644
index 0000000000..24dbe37567
--- /dev/null
+++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs
@@ -0,0 +1,53 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.ComponentModel;
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Input;
+
+namespace osu.Game.Graphics.UserInterface
+{
+ ///
+ /// Adds hover sounds to a drawable.
+ /// Does not draw anything.
+ ///
+ public class HoverSounds : CompositeDrawable
+ {
+ private SampleChannel sampleHover;
+
+ protected readonly HoverSampleSet SampleSet;
+
+ public HoverSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal)
+ {
+ SampleSet = sampleSet;
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ sampleHover?.Play();
+ return base.OnHover(state);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(AudioManager audio)
+ {
+ sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}");
+ }
+ }
+
+ public enum HoverSampleSet
+ {
+ [Description("")]
+ Loud,
+ [Description("-soft")]
+ Normal,
+ [Description("-softer")]
+ Soft
+ }
+}
diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs
index ccc23e3ff6..081e59d3a7 100644
--- a/osu.Game/Graphics/UserInterface/OsuButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuButton.cs
@@ -1,126 +1,18 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Collections.Generic;
-using OpenTK.Graphics;
-using osu.Framework.Allocation;
-using osu.Framework.Audio;
-using osu.Framework.Audio.Sample;
-using osu.Framework.Extensions.Color4Extensions;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input;
-using osu.Game.Graphics.Backgrounds;
-using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface
{
- public class OsuButton : Button, IFilterable
+ ///
+ /// A button with added default sound effects.
+ ///
+ public class OsuButton : Button
{
- private Box hover;
-
- private SampleChannel sampleClick;
- private SampleChannel sampleHover;
-
- protected Triangles Triangles;
-
public OsuButton()
{
- Height = 40;
- }
-
- protected override SpriteText CreateText() => new OsuSpriteText
- {
- Depth = -1,
- Origin = Anchor.Centre,
- Anchor = Anchor.Centre,
- Font = @"Exo2.0-Bold",
- };
-
- public override bool HandleInput => Action != null;
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours, AudioManager audio)
- {
- if (Action == null)
- Colour = OsuColour.Gray(0.5f);
-
- BackgroundColour = colours.BlueDark;
-
- Content.Masking = true;
- Content.CornerRadius = 5;
-
- AddRange(new Drawable[]
- {
- Triangles = new Triangles
- {
- RelativeSizeAxes = Axes.Both,
- ColourDark = colours.BlueDarker,
- ColourLight = colours.Blue,
- },
- hover = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Blending = BlendingMode.Additive,
- Colour = Color4.White.Opacity(0.1f),
- Alpha = 0,
- },
- });
-
- sampleClick = audio.Sample.Get(@"UI/generic-click");
- sampleHover = audio.Sample.Get(@"UI/generic-hover");
-
- Enabled.ValueChanged += enabled_ValueChanged;
- Enabled.TriggerChange();
- }
-
- private void enabled_ValueChanged(bool enabled)
- {
- this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
- }
-
- protected override bool OnClick(InputState state)
- {
- sampleClick?.Play();
- return base.OnClick(state);
- }
-
- protected override bool OnHover(InputState state)
- {
- sampleHover?.Play();
- hover.FadeIn(200);
- return base.OnHover(state);
- }
-
- protected override void OnHoverLost(InputState state)
- {
- hover.FadeOut(200);
- base.OnHoverLost(state);
- }
-
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
- {
- Content.ScaleTo(0.9f, 4000, Easing.OutQuint);
- return base.OnMouseDown(state, args);
- }
-
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
- {
- Content.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(state, args);
- }
-
- public IEnumerable FilterTerms => new[] { Text };
-
- public bool MatchingFilter
- {
- set
- {
- this.FadeTo(value ? 1 : 0);
- }
+ Add(new HoverClickSounds(HoverSampleSet.Loud));
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
index 68ff99e593..40ff1243dc 100644
--- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
@@ -70,7 +70,8 @@ namespace osu.Game.Graphics.UserInterface
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding { Right = 5 },
- }
+ },
+ new HoverClickSounds()
};
Nub.Current.BindTo(Current);
diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
index f605804aaa..4401b509fd 100644
--- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
@@ -33,7 +33,6 @@ namespace osu.Game.Graphics.UserInterface
if (accentColour == default(Color4))
accentColour = colours.PinkDarker;
updateAccentColour();
-
}
private void updateAccentColour()
@@ -137,6 +136,8 @@ namespace osu.Game.Graphics.UserInterface
nonAccentHoverColour = colours.PinkDarker;
nonAccentSelectedColour = Color4.Black.Opacity(0.5f);
updateColours();
+
+ AddInternal(new HoverClickSounds(HoverSampleSet.Soft));
}
protected override void UpdateForegroundColour()
@@ -183,7 +184,7 @@ namespace osu.Game.Graphics.UserInterface
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
- }
+ },
};
}
}
@@ -237,8 +238,10 @@ namespace osu.Game.Graphics.UserInterface
Origin = Anchor.CentreRight,
Margin = new MarginPadding { Right = 4 },
Size = new Vector2(20),
- }
+ },
};
+
+ AddInternal(new HoverClickSounds());
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs
index 3fd5481152..7f16d73613 100644
--- a/osu.Game/Graphics/UserInterface/OsuMenu.cs
+++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs
@@ -72,7 +72,7 @@ namespace osu.Game.Graphics.UserInterface
private void load(AudioManager audio)
{
sampleHover = audio.Sample.Get(@"UI/generic-hover");
- sampleClick = audio.Sample.Get(@"UI/generic-click");
+ sampleClick = audio.Sample.Get(@"UI/generic-select");
BackgroundColour = Color4.Transparent;
BackgroundColourHover = OsuColour.FromHex(@"172023");
diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
index 3dd3596c30..fd75269610 100644
--- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
+++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
@@ -88,7 +88,8 @@ namespace osu.Game.Graphics.UserInterface
{
Origin = Anchor.TopCentre,
Expanded = true,
- }
+ },
+ new HoverClickSounds()
};
Current.DisabledChanged += disabled =>
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
index b053195030..decbf57ad1 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
@@ -131,7 +131,8 @@ namespace osu.Game.Graphics.UserInterface
Colour = Color4.White,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
- }
+ },
+ new HoverClickSounds()
};
}
diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs
index 6b97e54ecd..c69857a5c4 100644
--- a/osu.Game/Graphics/UserInterface/PageTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs
@@ -57,6 +57,7 @@ namespace osu.Game.Graphics.UserInterface
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
},
+ new HoverClickSounds()
};
}
diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs
new file mode 100644
index 0000000000..61e9705064
--- /dev/null
+++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs
@@ -0,0 +1,108 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using OpenTK.Graphics;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input;
+using osu.Game.Graphics.Backgrounds;
+using osu.Game.Graphics.Sprites;
+
+namespace osu.Game.Graphics.UserInterface
+{
+ ///
+ /// A button with moving triangles in the background.
+ ///
+ public class TriangleButton : OsuButton, IFilterable
+ {
+ private Box hover;
+
+ protected Triangles Triangles;
+
+ public TriangleButton()
+ {
+ Height = 40;
+ }
+
+ protected override SpriteText CreateText() => new OsuSpriteText
+ {
+ Depth = -1,
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Font = @"Exo2.0-Bold",
+ };
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ BackgroundColour = colours.BlueDark;
+
+ Content.Masking = true;
+ Content.CornerRadius = 5;
+
+ AddRange(new Drawable[]
+ {
+ Triangles = new Triangles
+ {
+ RelativeSizeAxes = Axes.Both,
+ ColourDark = colours.BlueDarker,
+ ColourLight = colours.Blue,
+ },
+ hover = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Blending = BlendingMode.Additive,
+ Colour = Color4.White.Opacity(0.1f),
+ Alpha = 0,
+ },
+ });
+
+ Enabled.ValueChanged += enabled_ValueChanged;
+ Enabled.TriggerChange();
+ }
+
+ private void enabled_ValueChanged(bool enabled)
+ {
+ this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ hover.FadeIn(200);
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ hover.FadeOut(200);
+ base.OnHoverLost(state);
+ }
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ {
+ Content.ScaleTo(0.9f, 4000, Easing.OutQuint);
+ return base.OnMouseDown(state, args);
+ }
+
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ {
+ Content.ScaleTo(1, 1000, Easing.OutElastic);
+ return base.OnMouseUp(state, args);
+ }
+
+ public IEnumerable FilterTerms => new[] { Text };
+
+ public bool MatchingFilter
+ {
+ set
+ {
+ this.FadeTo(value ? 1 : 0);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs
index 5a9f609bca..cdcc06a65c 100644
--- a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs
+++ b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs
@@ -12,13 +12,16 @@ namespace osu.Game.Online.API.Requests
public Action DownloadProgressed;
- public DownloadBeatmapSetRequest(BeatmapSetInfo set)
+ private readonly bool noVideo;
+
+ public DownloadBeatmapSetRequest(BeatmapSetInfo set, bool noVideo)
{
+ this.noVideo = noVideo;
BeatmapSet = set;
Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total);
}
- protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download";
+ protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";
}
}
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 50639e3427..8eaa20f781 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using System.Reflection;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
using osu.Framework.Configuration;
using osu.Framework.Development;
using osu.Framework.Graphics;
@@ -137,6 +138,10 @@ namespace osu.Game
Beatmap = new NonNullableBindable(defaultBeatmap);
BeatmapManager.DefaultBeatmap = defaultBeatmap;
+ // tracks play so loud our samples can't keep up.
+ // this adds a global reduction of track volume for the time being.
+ Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
+
Beatmap.ValueChanged += b =>
{
var trackLoaded = lastBeatmap?.TrackLoaded ?? false;
diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs
index 96bb613f9f..4135aef268 100644
--- a/osu.Game/Overlays/BeatmapSet/Header.cs
+++ b/osu.Game/Overlays/BeatmapSet/Header.cs
@@ -29,8 +29,11 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Container noVideoButtons;
private readonly FillFlowContainer videoButtons;
private readonly AuthorInfo author;
+ private readonly Container downloadButtonsContainer;
public Details Details;
+ private BeatmapManager beatmaps;
+
private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker;
@@ -48,24 +51,22 @@ namespace osu.Game.Overlays.BeatmapSet
title.Text = BeatmapSet.Metadata.Title;
artist.Text = BeatmapSet.Metadata.Artist;
+ downloadButtonsContainer.FadeIn();
noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration);
videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration);
cover?.FadeOut(400, Easing.Out);
- coverContainer.Add(cover = new DelayedLoadWrapper(new BeatmapSetCover(BeatmapSet)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fill,
- OnLoadComplete = d =>
+ coverContainer.Add(cover = new DelayedLoadWrapper(
+ new BeatmapSetCover(BeatmapSet)
{
- d.FadeInFromZero(400, Easing.Out);
- },
- })
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fill,
+ OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
+ }, 300)
{
RelativeSizeAxes = Axes.Both,
- TimeBeforeLoad = 300
});
}
}
@@ -164,7 +165,7 @@ namespace osu.Game.Overlays.BeatmapSet
Children = new Drawable[]
{
new FavouriteButton(),
- new Container
+ downloadButtonsContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
@@ -174,7 +175,10 @@ namespace osu.Game.Overlays.BeatmapSet
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
- Child = new DownloadButton("Download", @""),
+ Child = new DownloadButton("Download", @"")
+ {
+ Action = () => download(false),
+ },
},
videoButtons = new FillFlowContainer
{
@@ -183,8 +187,14 @@ namespace osu.Game.Overlays.BeatmapSet
Alpha = 0f,
Children = new[]
{
- new DownloadButton("Download", "with Video"),
- new DownloadButton("Download", "without Video"),
+ new DownloadButton("Download", "with Video")
+ {
+ Action = () => download(false),
+ },
+ new DownloadButton("Download", "without Video")
+ {
+ Action = () => download(true),
+ },
},
},
},
@@ -208,9 +218,39 @@ namespace osu.Game.Overlays.BeatmapSet
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OsuColour colours, BeatmapManager beatmaps)
{
tabsBg.Colour = colours.Gray3;
+ this.beatmaps = beatmaps;
+
+ beatmaps.BeatmapSetAdded += handleBeatmapAdd;
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ if (beatmaps != null) beatmaps.BeatmapSetAdded -= handleBeatmapAdd;
+ }
+
+ private void handleBeatmapAdd(BeatmapSetInfo beatmap)
+ {
+ if (beatmap.OnlineBeatmapSetID == BeatmapSet?.OnlineBeatmapSetID)
+ downloadButtonsContainer.FadeOut(transition_duration);
+ }
+
+ private void download(bool noVideo)
+ {
+ if (beatmaps.GetExistingDownload(BeatmapSet) != null)
+ {
+ downloadButtonsContainer.MoveToX(-5, 50, Easing.OutSine).Then()
+ .MoveToX(5, 100, Easing.InOutSine).Then()
+ .MoveToX(-5, 100, Easing.InOutSine).Then()
+ .MoveToX(0, 50, Easing.InSine).Then();
+
+ return;
+ }
+
+ beatmaps.Download(BeatmapSet, noVideo);
}
}
}
diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs
index 4db6bdf5e4..32f933ff42 100644
--- a/osu.Game/Overlays/Chat/ChatLine.cs
+++ b/osu.Game/Overlays/Chat/ChatLine.cs
@@ -222,7 +222,7 @@ namespace osu.Game.Overlays.Chat
}
- private class MessageSender : ClickableContainer, IHasContextMenu
+ private class MessageSender : OsuClickableContainer, IHasContextMenu
{
private readonly User sender;
diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs
index 9f1028c168..f58ee8f819 100644
--- a/osu.Game/Overlays/Chat/ChatTabControl.cs
+++ b/osu.Game/Overlays/Chat/ChatTabControl.cs
@@ -17,6 +17,7 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using System;
+using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Chat
{
@@ -259,7 +260,7 @@ namespace osu.Game.Overlays.Chat
};
}
- public class CloseButton : ClickableContainer
+ public class CloseButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs
index ef89c0022b..a55de951f1 100644
--- a/osu.Game/Overlays/Direct/DirectPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectPanel.cs
@@ -16,9 +16,7 @@ using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
-using osu.Game.Online.API;
using osu.Framework.Logging;
-using osu.Game.Overlays.Notifications;
using osu.Game.Online.API.Requests;
using osu.Framework.Configuration;
using osu.Framework.Audio.Track;
@@ -35,10 +33,8 @@ namespace osu.Game.Overlays.Direct
private Container content;
- private APIAccess api;
private ProgressBar progressBar;
private BeatmapManager beatmaps;
- private NotificationOverlay notifications;
private BeatmapSetOverlay beatmapSetOverlay;
public Track Preview => PlayButton.Preview;
@@ -71,11 +67,9 @@ namespace osu.Game.Overlays.Direct
[BackgroundDependencyLoader(permitNulls: true)]
- private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications, BeatmapSetOverlay beatmapSetOverlay)
+ private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{
- this.api = api;
this.beatmaps = beatmaps;
- this.notifications = notifications;
this.beatmapSetOverlay = beatmapSetOverlay;
AddInternal(content = new Container
@@ -109,6 +103,14 @@ namespace osu.Game.Overlays.Direct
if (downloadRequest != null)
attachDownload(downloadRequest);
+
+ beatmaps.BeatmapDownloadBegan += attachDownload;
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ beatmaps.BeatmapDownloadBegan -= attachDownload;
}
protected override void Update()
@@ -151,16 +153,6 @@ namespace osu.Game.Overlays.Direct
protected void StartDownload()
{
- if (!api.LocalUser.Value.IsSupporter)
- {
- notifications.Post(new SimpleNotification
- {
- Icon = FontAwesome.fa_superpowers,
- Text = "You gotta be a supporter to download for now 'yo"
- });
- return;
- }
-
if (beatmaps.GetExistingDownload(SetInfo) != null)
{
// we already have an active download running.
@@ -172,13 +164,14 @@ namespace osu.Game.Overlays.Direct
return;
}
- var request = beatmaps.Download(SetInfo);
-
- attachDownload(request);
+ beatmaps.Download(SetInfo);
}
private void attachDownload(DownloadBeatmapSetRequest request)
{
+ if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID)
+ return;
+
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
@@ -219,21 +212,21 @@ namespace osu.Game.Overlays.Direct
return icons;
}
- protected Drawable CreateBackground() => new DelayedLoadWrapper(new BeatmapSetCover(SetInfo)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fill,
- OnLoadComplete = d =>
+ protected Drawable CreateBackground() => new DelayedLoadWrapper(
+ new BeatmapSetCover(SetInfo)
{
- d.FadeInFromZero(400, Easing.Out);
- BlackBackground.Delay(400).FadeOut();
- },
- })
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fill,
+ OnLoadComplete = d =>
+ {
+ d.FadeInFromZero(400, Easing.Out);
+ BlackBackground.Delay(400).FadeOut();
+ },
+ }, 300)
{
RelativeSizeAxes = Axes.Both,
- TimeBeforeLoad = 300
};
public class Statistic : FillFlowContainer
diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs
index 9317bc06cf..75a3358d51 100644
--- a/osu.Game/Overlays/Direct/PlayButton.cs
+++ b/osu.Game/Overlays/Direct/PlayButton.cs
@@ -146,18 +146,17 @@ namespace osu.Game.Overlays.Direct
loading = true;
- Add(new AsyncLoadWrapper(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3")
- {
- OnLoadComplete = d =>
+ LoadComponentAsync(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3"),
+ d =>
{
- // we may have been replaced by another loader
+ // We may have been replaced by another loader
if (trackLoader != d) return;
- Preview = (d as TrackLoader)?.Preview;
+ Preview = d?.Preview;
Playing.TriggerChange();
loading = false;
- },
- }));
+ Add(trackLoader);
+ });
}
private class TrackLoader : Drawable
diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs
index 6f7fabb910..0b7a30797d 100644
--- a/osu.Game/Overlays/DirectOverlay.cs
+++ b/osu.Game/Overlays/DirectOverlay.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Overlays
{
if (beatmapSets?.Equals(value) ?? false) return;
- beatmapSets = value;
+ beatmapSets = value?.ToList();
if (beatmapSets == null) return;
@@ -65,8 +65,6 @@ namespace osu.Game.Overlays
}
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
-
- recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
}
}
@@ -222,7 +220,11 @@ namespace osu.Game.Overlays
switch (displayStyle)
{
case PanelDisplayStyle.Grid:
- return new DirectGridPanel(b);
+ return new DirectGridPanel(b)
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ };
default:
return new DirectListPanel(b);
}
@@ -282,7 +284,11 @@ namespace osu.Game.Overlays
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList();
// may not need scheduling; loads async internally.
- Schedule(() => BeatmapSets = sets);
+ Schedule(() =>
+ {
+ BeatmapSets = sets;
+ recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
+ });
});
};
diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs
index 30ff0ab026..c670cc0153 100644
--- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs
+++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Overlays.KeyBinding
}
}
- public class ResetButton : OsuButton
+ public class ResetButton : TriangleButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs
index 0ead4ea019..77b7c3add2 100644
--- a/osu.Game/Overlays/Mods/ModButton.cs
+++ b/osu.Game/Overlays/Mods/ModButton.cs
@@ -17,6 +17,7 @@ using osu.Game.Rulesets.UI;
using System;
using System.Linq;
using osu.Framework.Graphics.Cursor;
+using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Mods
{
@@ -148,7 +149,7 @@ namespace osu.Game.Overlays.Mods
// the mods from Mod, only multiple if Mod is a MultiMod
- public override Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex);
+ public virtual Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex);
[BackgroundDependencyLoader]
private void load(AudioManager audio)
@@ -253,6 +254,7 @@ namespace osu.Game.Overlays.Mods
Anchor = Anchor.TopCentre,
TextSize = 18,
},
+ new HoverClickSounds()
};
Mod = mod;
diff --git a/osu.Game/Overlays/Mods/ModButtonEmpty.cs b/osu.Game/Overlays/Mods/ModButtonEmpty.cs
index 638c2a0e47..f776c174d2 100644
--- a/osu.Game/Overlays/Mods/ModButtonEmpty.cs
+++ b/osu.Game/Overlays/Mods/ModButtonEmpty.cs
@@ -3,7 +3,6 @@
using OpenTK;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Mods;
namespace osu.Game.Overlays.Mods
{
@@ -12,8 +11,6 @@ namespace osu.Game.Overlays.Mods
///
public class ModButtonEmpty : Container
{
- public virtual Mod SelectedMod => null;
-
public ModButtonEmpty()
{
Size = new Vector2(100f);
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index 22e34be34c..c7bc5c1d93 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -316,19 +316,16 @@ namespace osu.Game.Overlays.Profile
private void loadUser()
{
- coverContainer.Add(new AsyncLoadWrapper(new UserCoverBackground(user)
+ LoadComponentAsync(new UserCoverBackground(user)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
- OnLoadComplete = d => d.FadeInFromZero(200)
- })
- {
- Masking = true,
- RelativeSizeAxes = Axes.Both,
- Depth = float.MaxValue
- });
+ OnLoadComplete = d => d.FadeInFromZero(200),
+ Depth = float.MaxValue,
+ },
+ coverContainer.Add);
if (user.IsSupporter) supporterTag.Show();
diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
new file mode 100644
index 0000000000..8a835634b8
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
@@ -0,0 +1,134 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK;
+using OpenTK.Graphics;
+using osu.Framework.Configuration;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Users;
+
+namespace osu.Game.Overlays.Profile.Sections.Kudosu
+{
+ public class KudosuInfo : Container
+ {
+ private readonly Bindable user = new Bindable();
+
+ public KudosuInfo(Bindable user)
+ {
+ this.user.BindTo(user);
+
+ CountSection total;
+ CountSection avaliable;
+
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ Masking = true;
+ CornerRadius = 3;
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Shadow,
+ Offset = new Vector2(0f, 1f),
+ Radius = 3f,
+ Colour = Color4.Black.Opacity(0.2f),
+ };
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.Gray(0.2f)
+ },
+ new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new[]
+ {
+ total = new CountSection(
+ "Total Kudosu Earned",
+ "Based on how much of a contribution the user has made to beatmap moderation. See this link for more information."
+ ),
+ avaliable = new CountSection(
+ "Kudosu Avaliable",
+ "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."
+ ),
+ }
+ }
+ };
+
+ this.user.ValueChanged += newUser =>
+ {
+ total.Count = newUser?.Kudosu.Total ?? 0;
+ avaliable.Count = newUser?.Kudosu.Available ?? 0;
+ };
+ }
+
+ protected override bool OnClick(InputState state) => true;
+
+ private class CountSection : Container
+ {
+ private readonly OsuSpriteText valueText;
+
+ public int Count
+ {
+ set { valueText.Text = value.ToString(); }
+ }
+
+ public CountSection(string header, string description)
+ {
+ RelativeSizeAxes = Axes.X;
+ Width = 0.5f;
+ AutoSizeAxes = Axes.Y;
+ Padding = new MarginPadding { Horizontal = 10, Top = 10, Bottom = 20 };
+ Child = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 5),
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(5, 0),
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Text = header + ":",
+ TextSize = 20,
+ Font = @"Exo2.0-RegularItalic",
+ },
+ valueText = new OsuSpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Text = "0",
+ TextSize = 40,
+ UseFullGlyphHeight = false,
+ Font = @"Exo2.0-RegularItalic"
+ }
+ }
+ },
+ new TextFlowContainer(t => { t.TextSize = 19; })
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Text = description
+ }
+ }
+ };
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs
index 3c36368fd7..907c26e5e8 100644
--- a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs
+++ b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs
@@ -1,6 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Overlays.Profile.Sections.Kudosu;
+
namespace osu.Game.Overlays.Profile.Sections
{
public class KudosuSection : ProfileSection
@@ -8,5 +10,13 @@ namespace osu.Game.Overlays.Profile.Sections
public override string Title => "Kudosu!";
public override string Identifier => "kudosu";
+
+ public KudosuSection()
+ {
+ Children = new[]
+ {
+ new KudosuInfo(User),
+ };
+ }
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
index 07a8e7464a..9875ee8004 100644
--- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
@@ -17,6 +17,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{
Children = new Drawable[]
{
+ new SettingsCheckbox
+ {
+ LabelText = "Show converted beatmaps",
+ Bindable = config.GetBindable(OsuSetting.ShowConvertedBeatmaps),
+ },
new SettingsSlider
{
LabelText = "Display beatmaps from",
@@ -33,7 +38,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{
LabelText = "Random beatmap selection",
Bindable = config.GetBindable(OsuSetting.SelectionRandomType),
- },
+ }
};
}
diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs
index 4c82a9ae4b..4f4f381ae1 100644
--- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs
@@ -12,9 +12,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
{
public class GeneralSettings : SettingsSubsection
{
- private OsuButton importButton;
- private OsuButton deleteButton;
- private OsuButton restoreButton;
+ private TriangleButton importButton;
+ private TriangleButton deleteButton;
+ private TriangleButton restoreButton;
protected override string Header => "General";
diff --git a/osu.Game/Overlays/Settings/SettingsButton.cs b/osu.Game/Overlays/Settings/SettingsButton.cs
index 5320cef850..19493f6c70 100644
--- a/osu.Game/Overlays/Settings/SettingsButton.cs
+++ b/osu.Game/Overlays/Settings/SettingsButton.cs
@@ -6,7 +6,7 @@ using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
{
- public class SettingsButton : OsuButton
+ public class SettingsButton : TriangleButton
{
public SettingsButton()
{
diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs
index b39c8ab7cf..4b8366f0fc 100644
--- a/osu.Game/Overlays/Settings/SidebarButton.cs
+++ b/osu.Game/Overlays/Settings/SidebarButton.cs
@@ -12,14 +12,14 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
{
- public class SidebarButton : Container
+ public class SidebarButton : OsuButton
{
private readonly SpriteIcon drawableIcon;
private readonly SpriteText headerText;
- private readonly Box backgroundBox;
private readonly Box selectionIndicator;
private readonly Container text;
public Action Action;
@@ -61,17 +61,14 @@ namespace osu.Game.Overlays.Settings
public SidebarButton()
{
+ BackgroundColour = OsuColour.Gray(60);
+ Background.Alpha = 0;
+
Height = Sidebar.DEFAULT_WIDTH;
RelativeSizeAxes = Axes.X;
- Children = new Drawable[]
+
+ AddRange(new Drawable[]
{
- backgroundBox = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Blending = BlendingMode.Additive,
- Colour = OsuColour.Gray(60),
- Alpha = 0,
- },
text = new Container
{
Width = Sidebar.DEFAULT_WIDTH,
@@ -101,7 +98,7 @@ namespace osu.Game.Overlays.Settings
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
}
- };
+ });
}
[BackgroundDependencyLoader]
@@ -113,20 +110,19 @@ namespace osu.Game.Overlays.Settings
protected override bool OnClick(InputState state)
{
Action?.Invoke(section);
- backgroundBox.FlashColour(Color4.White, 400);
- return true;
+ return base.OnClick(state);
}
protected override bool OnHover(InputState state)
{
- backgroundBox.FadeTo(0.4f, 200);
+ Background.FadeTo(0.4f, 200);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
- backgroundBox.FadeTo(0, 200);
+ Background.FadeTo(0, 200);
base.OnHoverLost(state);
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs
index cb17216679..c039f9d311 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs
@@ -13,6 +13,7 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Toolbar
{
@@ -74,7 +75,7 @@ namespace osu.Game.Overlays.Toolbar
private readonly SpriteText tooltip2;
protected FillFlowContainer Flow;
- public ToolbarButton()
+ public ToolbarButton() : base(HoverSampleSet.Loud)
{
Width = WIDTH;
RelativeSizeAxes = Axes.Y;
@@ -195,4 +196,4 @@ namespace osu.Game.Overlays.Toolbar
};
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs
index dd31a43290..7374a9aa44 100644
--- a/osu.Game/Overlays/UserProfileOverlay.cs
+++ b/osu.Game/Overlays/UserProfileOverlay.cs
@@ -97,7 +97,7 @@ namespace osu.Game.Overlays
//new MedalsSection(),
new HistoricalSection(),
new BeatmapsSection(),
- //new KudosuSection()
+ new KudosuSection()
};
tabs = new ProfileTabControl
{
diff --git a/osu.Game/Rulesets/Replays/ReplayFrame.cs b/osu.Game/Rulesets/Replays/ReplayFrame.cs
index b0f62e5271..02c969f648 100644
--- a/osu.Game/Rulesets/Replays/ReplayFrame.cs
+++ b/osu.Game/Rulesets/Replays/ReplayFrame.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Replays
{
public Vector2 Position => new Vector2(MouseX ?? 0, MouseY ?? 0);
- public bool IsImportant => MouseX.HasValue && MouseY.HasValue && (MouseLeft || MouseRight);
+ public virtual bool IsImportant => MouseX.HasValue && MouseY.HasValue && (MouseLeft || MouseRight);
public float? MouseX;
public float? MouseY;
@@ -68,4 +68,4 @@ namespace osu.Game.Rulesets.Replays
return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}";
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index ed2fdf4157..d787da6a0a 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -10,6 +10,7 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets
@@ -49,6 +50,8 @@ namespace osu.Game.Rulesets
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null);
+ public virtual PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => null;
+
public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle };
public abstract string Description { get; }
diff --git a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs
new file mode 100644
index 0000000000..4f603049db
--- /dev/null
+++ b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs
@@ -0,0 +1,35 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Objects;
+
+namespace osu.Game.Rulesets.Scoring
+{
+ public abstract class PerformanceCalculator
+ {
+ public abstract double Calculate(Dictionary categoryDifficulty = null);
+ }
+
+ public abstract class PerformanceCalculator : PerformanceCalculator
+ where TObject : HitObject
+ {
+ private readonly Dictionary attributes = new Dictionary();
+ protected IDictionary Attributes => attributes;
+
+ protected readonly Beatmap Beatmap;
+ protected readonly Score Score;
+
+ protected PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score)
+ {
+ Beatmap = CreateBeatmapConverter().Convert(beatmap);
+ Score = score;
+
+ var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods);
+ diffCalc.Calculate(attributes);
+ }
+
+ protected abstract BeatmapConverter CreateBeatmapConverter();
+ }
+}
diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs
index ccd61643ce..5e55166c19 100644
--- a/osu.Game/Screens/Menu/Button.cs
+++ b/osu.Game/Screens/Menu/Button.cs
@@ -174,7 +174,7 @@ namespace osu.Game.Screens.Menu
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
- sampleHover = audio.Sample.Get(@"Menu/hover");
+ sampleHover = audio.Sample.Get(@"Menu/button-hover");
if (!string.IsNullOrEmpty(sampleName))
sampleClick = audio.Sample.Get($@"Menu/{sampleName}");
}
diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs
index 5a4a5f07b5..ce7856c5a9 100644
--- a/osu.Game/Screens/Menu/ButtonSystem.cs
+++ b/osu.Game/Screens/Menu/ButtonSystem.cs
@@ -117,13 +117,13 @@ namespace osu.Game.Screens.Menu
},
};
- buttonsPlay.Add(new Button(@"solo", @"select-6", FontAwesome.fa_user, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
- buttonsPlay.Add(new Button(@"multi", @"select-5", FontAwesome.fa_users, new Color4(94, 63, 186, 255), () => OnMulti?.Invoke(), 0, Key.M));
- buttonsPlay.Add(new Button(@"chart", @"select-5", FontAwesome.fa_osu_charts, new Color4(80, 53, 160, 255), () => OnChart?.Invoke()));
+ buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.fa_user, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
+ buttonsPlay.Add(new Button(@"multi", @"button-generic-select", FontAwesome.fa_users, new Color4(94, 63, 186, 255), () => OnMulti?.Invoke(), 0, Key.M));
+ buttonsPlay.Add(new Button(@"chart", @"button-generic-select", FontAwesome.fa_osu_charts, new Color4(80, 53, 160, 255), () => OnChart?.Invoke()));
- buttonsTopLevel.Add(new Button(@"play", @"select-1", FontAwesome.fa_osu_logo, new Color4(102, 68, 204, 255), onPlay, WEDGE_WIDTH, Key.P));
- buttonsTopLevel.Add(new Button(@"osu!editor", @"select-5", FontAwesome.fa_osu_edit_o, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
- buttonsTopLevel.Add(new Button(@"osu!direct", string.Empty, FontAwesome.fa_osu_chevron_down_o, new Color4(165, 204, 0, 255), () => OnDirect?.Invoke(), 0, Key.D));
+ buttonsTopLevel.Add(new Button(@"play", @"button-play-select", FontAwesome.fa_osu_logo, new Color4(102, 68, 204, 255), onPlay, WEDGE_WIDTH, Key.P));
+ buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", FontAwesome.fa_osu_edit_o, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
+ buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", FontAwesome.fa_osu_chevron_down_o, new Color4(165, 204, 0, 255), () => OnDirect?.Invoke(), 0, Key.D));
buttonsTopLevel.Add(new Button(@"exit", string.Empty, FontAwesome.fa_osu_cross_o, new Color4(238, 51, 153, 255), onExit, 0, Key.Q));
buttonFlow.AddRange(buttonsPlay);
@@ -134,7 +134,7 @@ namespace osu.Game.Screens.Menu
private void load(AudioManager audio, OsuGame game = null)
{
toolbar = game?.Toolbar;
- sampleBack = audio.Sample.Get(@"Menu/select-4");
+ sampleBack = audio.Sample.Get(@"Menu/button-back-select");
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
@@ -180,19 +180,21 @@ namespace osu.Game.Screens.Menu
State = MenuState.TopLevel;
}
- private void onOsuLogo()
+ private bool onOsuLogo()
{
switch (state)
{
+ default:
+ return true;
case MenuState.Initial:
State = MenuState.TopLevel;
- return;
+ return true;
case MenuState.TopLevel:
buttonsTopLevel.First().TriggerOnClick();
- return;
+ return false;
case MenuState.Play:
buttonsPlay.First().TriggerOnClick();
- return;
+ return false;
}
}
diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs
index 252f2d37b5..9ca12702e5 100644
--- a/osu.Game/Screens/Menu/OsuLogo.cs
+++ b/osu.Game/Screens/Menu/OsuLogo.cs
@@ -48,7 +48,10 @@ namespace osu.Game.Screens.Menu
private readonly Triangles triangles;
- public Action Action;
+ ///
+ /// Return value decides whether the logo should play its own sample for the click action.
+ ///
+ public Func Action;
public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.74f;
@@ -248,8 +251,8 @@ namespace osu.Game.Screens.Menu
[BackgroundDependencyLoader]
private void load(TextureStore textures, AudioManager audio)
{
- sampleClick = audio.Sample.Get(@"Menu/select-2");
- sampleBeat = audio.Sample.Get(@"Menu/heartbeat");
+ sampleClick = audio.Sample.Get(@"Menu/osu-logo-select");
+ sampleBeat = audio.Sample.Get(@"Menu/osu-logo-heartbeat");
logo.Texture = textures.Get(@"Menu/logo");
ripple.Texture = textures.Get(@"Menu/logo");
@@ -354,13 +357,12 @@ namespace osu.Game.Screens.Menu
{
if (!interactive) return false;
- sampleClick.Play();
+ if (Action?.Invoke() ?? true)
+ sampleClick.Play();
flashLayer.ClearTransforms();
flashLayer.Alpha = 0.4f;
flashLayer.FadeOut(1500, Easing.OutExpo);
-
- Action?.Invoke();
return true;
}
diff --git a/osu.Game/Screens/Multiplayer/DrawableRoom.cs b/osu.Game/Screens/Multiplayer/DrawableRoom.cs
index d2f88224c2..0ba4aaa364 100644
--- a/osu.Game/Screens/Multiplayer/DrawableRoom.cs
+++ b/osu.Game/Screens/Multiplayer/DrawableRoom.cs
@@ -228,16 +228,16 @@ namespace osu.Game.Screens.Multiplayer
if (value != null)
{
coverContainer.FadeIn(transition_duration);
- coverContainer.Children = new[]
+
+
+ LoadComponentAsync(new BeatmapSetCover(value.BeatmapSet)
{
- new AsyncLoadWrapper(new BeatmapSetCover(value.BeatmapSet)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- FillMode = FillMode.Fill,
- OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
- }) { RelativeSizeAxes = Axes.Both },
- };
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ FillMode = FillMode.Fill,
+ OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
+ },
+ coverContainer.Add);
beatmapTitle.Current = localisation.GetUnicodePreference(value.Metadata.TitleUnicode, value.Metadata.Title);
beatmapDash.Text = @" - ";
diff --git a/osu.Game/Screens/Multiplayer/RoomInspector.cs b/osu.Game/Screens/Multiplayer/RoomInspector.cs
index 66ce51b428..8d7401500f 100644
--- a/osu.Game/Screens/Multiplayer/RoomInspector.cs
+++ b/osu.Game/Screens/Multiplayer/RoomInspector.cs
@@ -329,17 +329,16 @@ namespace osu.Game.Screens.Multiplayer
if (value != null)
{
coverContainer.FadeIn(transition_duration);
- coverContainer.Children = new[]
+
+ LoadComponentAsync(new BeatmapSetCover(value.BeatmapSet)
{
- new AsyncLoadWrapper(new BeatmapSetCover(value.BeatmapSet)
- {
- RelativeSizeAxes = Axes.Both,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- FillMode = FillMode.Fill,
- OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
- }) { RelativeSizeAxes = Axes.Both },
- };
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ FillMode = FillMode.Fill,
+ OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
+ },
+ coverContainer.Add);
beatmapTitle.Current = localisation.GetUnicodePreference(value.Metadata.TitleUnicode, value.Metadata.Title);
beatmapDash.Text = @" - ";
diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs
index f5ff9ea036..76ee4a607e 100644
--- a/osu.Game/Screens/OsuScreen.cs
+++ b/osu.Game/Screens/OsuScreen.cs
@@ -70,7 +70,7 @@ namespace osu.Game.Screens
if (osuGame != null)
Ruleset.BindTo(osuGame.Ruleset);
- sampleExit = audio.Sample.Get(@"UI/melodic-1");
+ sampleExit = audio.Sample.Get(@"UI/screen-back");
}
protected override void OnResuming(Screen last)
diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs
index eed5cd1c20..5f5eeb63a0 100644
--- a/osu.Game/Screens/Play/PauseContainer.cs
+++ b/osu.Game/Screens/Play/PauseContainer.cs
@@ -22,8 +22,6 @@ namespace osu.Game.Screens.Play
{
public bool IsPaused { get; private set; }
- public bool AllowExit => IsPaused && pauseOverlay.Alpha == 1;
-
public Func CheckCanPause;
private const double pause_cooldown = 1000;
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index cd2818398d..dc746b305c 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -46,6 +46,8 @@ namespace osu.Game.Screens.Play
public bool HasFailed { get; private set; }
+ public bool AllowPause { get; set; } = true;
+
public int RestartCount;
private IAdjustableClock adjustableSourceClock;
@@ -158,7 +160,7 @@ namespace osu.Game.Screens.Play
FramedClock = offsetClock,
OnRetry = Restart,
OnQuit = Exit,
- CheckCanPause = () => ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
+ CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
Retries = RestartCount,
OnPause = () => {
hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
@@ -248,7 +250,10 @@ namespace osu.Game.Screens.Play
storyboard = beatmap.Storyboard.CreateDrawable(Beatmap.Value);
storyboard.Masking = true;
- storyboardContainer.Add(asyncLoad ? new AsyncLoadWrapper(storyboard) { RelativeSizeAxes = Axes.Both } : (Drawable)storyboard);
+ if (asyncLoad)
+ LoadComponentAsync(storyboard, storyboardContainer.Add);
+ else
+ storyboardContainer.Add(storyboard);
}
public void Restart()
@@ -305,7 +310,7 @@ namespace osu.Game.Screens.Play
if (!loadedSuccessfully)
return;
- (Background as BackgroundScreenBeatmap)?.BlurTo(Vector2.Zero, 1500, Easing.OutQuint);
+ (Background as BackgroundScreenBeatmap)?.BlurTo(Vector2.Zero, 1000, Easing.OutQuint);
dimLevel.ValueChanged += dimLevel_ValueChanged;
showStoryboard.ValueChanged += showStoryboard_ValueChanged;
@@ -352,7 +357,7 @@ namespace osu.Game.Screens.Play
protected override bool OnExiting(Screen next)
{
- if (HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || RulesetContainer?.HasReplayLoaded != false)
+ if (!AllowPause || HasFailed || !ValidForResume || pauseContainer?.IsPaused != false || RulesetContainer?.HasReplayLoaded != false)
{
// In the case of replays, we may have changed the playback rate.
applyRateFromMods();
diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs
index d7c509d979..a9a778fe17 100644
--- a/osu.Game/Screens/Select/BeatmapDetails.cs
+++ b/osu.Game/Screens/Select/BeatmapDetails.cs
@@ -261,6 +261,7 @@ namespace osu.Game.Screens.Select
description.Text = null;
source.Text = null;
tags.Text = null;
+
advanced.Beatmap = new BeatmapInfo
{
StarDifficulty = 0,
@@ -306,36 +307,16 @@ namespace osu.Game.Screens.Select
private class MetadataSection : Container
{
- private readonly TextFlowContainer textFlow;
-
- public string Text
- {
- set
- {
- if (string.IsNullOrEmpty(value))
- {
- this.FadeOut(transition_duration);
- return;
- }
-
- this.FadeIn(transition_duration);
- textFlow.Clear();
- textFlow.AddText(value, s => s.TextSize = 14);
- }
- }
-
- public Color4 TextColour
- {
- get { return textFlow.Colour; }
- set { textFlow.Colour = value; }
- }
+ private readonly FillFlowContainer textContainer;
+ private TextFlowContainer textFlow;
public MetadataSection(string title)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
+ Alpha = 0;
- InternalChild = new FillFlowContainer
+ InternalChild = textContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
@@ -361,6 +342,44 @@ namespace osu.Game.Screens.Select
},
};
}
+
+ public string Text
+ {
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ this.FadeOut(transition_duration);
+ return;
+ }
+
+ setTextAsync(value);
+ }
+ }
+
+ private void setTextAsync(string text)
+ {
+ LoadComponentAsync(new TextFlowContainer(s => s.TextSize = 14)
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Colour = textFlow.Colour,
+ Text = text
+ }, loaded =>
+ {
+ textFlow?.Expire();
+ textContainer.Add(textFlow = loaded);
+
+ // fade in if we haven't yet.
+ this.FadeIn(transition_duration);
+ });
+ }
+
+ public Color4 TextColour
+ {
+ get { return textFlow.Colour; }
+ set { textFlow.Colour = value; }
+ }
}
private class DimmedLoadingAnimation : VisibilityContainer
diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs
index 2153eb150c..19bcad367e 100644
--- a/osu.Game/Screens/Select/Details/UserRatings.cs
+++ b/osu.Game/Screens/Select/Details/UserRatings.cs
@@ -29,11 +29,17 @@ namespace osu.Game.Screens.Select.Details
if (value == metrics) return;
metrics = value;
- var ratings = Metrics.Ratings.ToList();
- negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2 + 1).Sum().ToString();
- positiveRatings.Text = ratings.GetRange(ratings.Count / 2 + 1, ratings.Count / 2).Sum().ToString();
- ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2 + 1).Sum() / ratings.Sum();
- graph.Values = Metrics.Ratings.Select(r => (float)r);
+ const int rating_range = 10;
+
+ var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0.
+
+ var negativeCount = ratings.Take(rating_range / 2).Sum();
+ var totalCount = ratings.Sum();
+
+ negativeRatings.Text = negativeCount.ToString();
+ positiveRatings.Text = (totalCount - negativeCount).ToString();
+ ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount;
+ graph.Values = ratings.Take(rating_range).Select(r => (float)r);
}
}
diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs
index e83613125b..1b86cec613 100644
--- a/osu.Game/Screens/Select/FilterControl.cs
+++ b/osu.Game/Screens/Select/FilterControl.cs
@@ -15,6 +15,7 @@ using osu.Game.Screens.Select.Filter;
using Container = osu.Framework.Graphics.Containers.Container;
using osu.Framework.Input;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Configuration;
using osu.Game.Rulesets;
namespace osu.Game.Screens.Select
@@ -60,6 +61,7 @@ namespace osu.Game.Screens.Select
Group = group,
Sort = sort,
SearchText = searchTextBox.Text,
+ AllowConvertedBeatmaps = showConverted,
Ruleset = ruleset
};
@@ -163,17 +165,24 @@ namespace osu.Game.Screens.Select
private readonly Bindable ruleset = new Bindable();
+ private Bindable showConverted;
+
[BackgroundDependencyLoader(permitNulls: true)]
- private void load(OsuColour colours, OsuGame osu)
+ private void load(OsuColour colours, OsuGame osu, OsuConfigManager config)
{
sortTabs.AccentColour = colours.GreenLight;
+ showConverted = config.GetBindable(OsuSetting.ShowConvertedBeatmaps);
+ showConverted.ValueChanged += val => updateCriteria();
+
if (osu != null)
ruleset.BindTo(osu.Ruleset);
- ruleset.ValueChanged += val => FilterChanged?.Invoke(CreateCriteria());
+ ruleset.ValueChanged += val => updateCriteria();
ruleset.TriggerChange();
}
+ private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
+
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
protected override bool OnMouseMove(InputState state) => true;
@@ -182,4 +191,4 @@ namespace osu.Game.Screens.Select
protected override bool OnDragStart(InputState state) => true;
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs
index 6c1fb1703d..c1355bfa63 100644
--- a/osu.Game/Screens/Select/FilterCriteria.cs
+++ b/osu.Game/Screens/Select/FilterCriteria.cs
@@ -16,6 +16,7 @@ namespace osu.Game.Screens.Select
public SortMode Sort;
public string SearchText;
public RulesetInfo Ruleset;
+ public bool AllowConvertedBeatmaps;
public void Filter(List groups)
{
@@ -23,7 +24,7 @@ namespace osu.Game.Screens.Select
{
var set = g.BeatmapSet;
- bool hasCurrentMode = set.Beatmaps.Any(bm => bm.RulesetID == (Ruleset?.ID ?? 0));
+ bool hasCurrentMode = AllowConvertedBeatmaps || set.Beatmaps.Any(bm => bm.RulesetID == (Ruleset?.ID ?? 0));
bool match = hasCurrentMode;
diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
index 6f3c19dd0c..377f32184d 100644
--- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Screens.Select.Leaderboards
private Box background;
private Container content;
- private Container avatar;
+ private Drawable avatar;
private DrawableRank scoreRank;
private OsuSpriteText nameLabel;
private GlowingSpriteText scoreLabel;
@@ -98,7 +98,7 @@ namespace osu.Game.Screens.Select.Leaderboards
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(edge_margin),
- Children = new Drawable[]
+ Children = new[]
{
avatar = new DelayedLoadWrapper(
new Avatar(Score.User)
@@ -115,7 +115,6 @@ namespace osu.Game.Screens.Select.Leaderboards
},
})
{
- TimeBeforeLoad = 500,
RelativeSizeAxes = Axes.None,
Size = new Vector2(HEIGHT - edge_margin * 2, HEIGHT - edge_margin * 2),
},
@@ -212,7 +211,7 @@ namespace osu.Game.Screens.Select.Leaderboards
public override void Show()
{
- foreach (var d in new Drawable[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer })
+ foreach (var d in new[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer })
d.FadeOut();
Alpha = 0;
diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs
index e0a3693371..bba6ddf577 100644
--- a/osu.Game/Screens/Select/PlaySongSelect.cs
+++ b/osu.Game/Screens/Select/PlaySongSelect.cs
@@ -4,6 +4,8 @@
using System.Linq;
using OpenTK.Input;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
@@ -42,9 +44,13 @@ namespace osu.Game.Screens.Select
beatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s));
}
+ private SampleChannel sampleConfirm;
+
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OsuColour colours, AudioManager audio)
{
+ sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection");
+
Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue);
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
@@ -128,6 +134,8 @@ namespace osu.Game.Screens.Select
Beatmap.Value.Track.Looping = false;
Beatmap.Disabled = true;
+ sampleConfirm?.Play();
+
LoadComponentAsync(player = new PlayerLoader(new Player()), l => Push(player));
}
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 5500d06136..6fcaff7976 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -203,15 +203,15 @@ namespace osu.Game.Screens.Select
Push(new Editor());
}
- private void onBeatmapRestored(BeatmapInfo b) => carousel.UpdateBeatmap(b);
- private void onBeatmapHidden(BeatmapInfo b) => carousel.UpdateBeatmap(b);
+ private void onBeatmapRestored(BeatmapInfo b) => Schedule(() => carousel.UpdateBeatmap(b));
+ private void onBeatmapHidden(BeatmapInfo b) => Schedule(() => carousel.UpdateBeatmap(b));
private void carouselBeatmapsLoaded()
{
if (Beatmap.Value.BeatmapSetInfo?.DeletePending == false)
carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false);
else
- carousel.SelectNext();
+ carousel.SelectNextRandom();
}
private void carouselRaisedStart(InputState state = null)
@@ -332,7 +332,11 @@ namespace osu.Game.Screens.Select
logo.FadeIn(logo_transition, Easing.OutQuint);
logo.ScaleTo(0.4f, logo_transition, Easing.OutQuint);
- logo.Action = () => carouselRaisedStart();
+ logo.Action = () =>
+ {
+ carouselRaisedStart();
+ return false;
+ };
}
protected override void LogoExiting(OsuLogo logo)
@@ -413,7 +417,7 @@ namespace osu.Game.Screens.Select
if (backgroundModeBeatmap != null)
{
backgroundModeBeatmap.Beatmap = beatmap;
- backgroundModeBeatmap.BlurTo(background_blur, 1000);
+ backgroundModeBeatmap.BlurTo(background_blur, 750, Easing.OutQuint);
backgroundModeBeatmap.FadeTo(1, 250);
}
diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs
index 269b206a78..6a8a361ea0 100644
--- a/osu.Game/Screens/Tournament/Drawings.cs
+++ b/osu.Game/Screens/Tournament/Drawings.cs
@@ -193,21 +193,21 @@ namespace osu.Game.Screens.Tournament
Children = new Drawable[]
{
- new OsuButton
+ new TriangleButton
{
RelativeSizeAxes = Axes.X,
Text = "Begin random",
Action = teamsContainer.StartScrolling,
},
- new OsuButton
+ new TriangleButton
{
RelativeSizeAxes = Axes.X,
Text = "Stop random",
Action = teamsContainer.StopScrolling,
},
- new OsuButton
+ new TriangleButton
{
RelativeSizeAxes = Axes.X,
@@ -232,7 +232,7 @@ namespace osu.Game.Screens.Tournament
Children = new Drawable[]
{
- new OsuButton
+ new TriangleButton
{
RelativeSizeAxes = Axes.X,
diff --git a/osu.Game/Tests/Visual/ScreenTestCase.cs b/osu.Game/Tests/Visual/ScreenTestCase.cs
new file mode 100644
index 0000000000..2f0831d84a
--- /dev/null
+++ b/osu.Game/Tests/Visual/ScreenTestCase.cs
@@ -0,0 +1,48 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Screens;
+using osu.Game.Screens;
+
+namespace osu.Game.Tests.Visual
+{
+ ///
+ /// A test case which can be used to test a screen (that relies on OnEntering being called to execute startup instructions).
+ ///
+ public abstract class ScreenTestCase : OsuTestCase
+ {
+ private readonly TestOsuScreen baseScreen;
+
+ protected ScreenTestCase()
+ {
+ Add(baseScreen = new TestOsuScreen());
+ }
+
+ protected void LoadScreen(OsuScreen screen) => baseScreen.LoadScreen(screen);
+
+ public class TestOsuScreen : OsuScreen
+ {
+ private OsuScreen nextScreen;
+
+ public void LoadScreen(OsuScreen screen) => Schedule(() =>
+ {
+ nextScreen = screen;
+
+ if (IsCurrentScreen)
+ {
+ Push(screen);
+ nextScreen = null;
+ }
+ else
+ MakeCurrent();
+ });
+
+ protected override void OnResuming(Screen last)
+ {
+ base.OnResuming(last);
+ if (nextScreen != null)
+ LoadScreen(nextScreen);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs
new file mode 100644
index 0000000000..6da14e9b12
--- /dev/null
+++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs
@@ -0,0 +1,395 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using System.Linq;
+using OpenTK;
+using OpenTK.Graphics;
+using osu.Framework.Allocation;
+using osu.Framework.Caching;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Tests.Visual
+{
+ public abstract class TestCasePerformancePoints : OsuTestCase
+ {
+ protected TestCasePerformancePoints(Ruleset ruleset)
+ {
+ Child = new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ Alpha = 0.5f,
+ },
+ new ScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new BeatmapList(ruleset)
+ }
+ }
+ },
+ null,
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ Alpha = 0.5f,
+ },
+ new ScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new StarRatingGrid()
+ }
+ }
+ },
+ null,
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ Alpha = 0.5f,
+ },
+ new ScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new PerformanceList()
+ }
+ }
+ },
+ }
+ },
+ ColumnDimensions = new[]
+ {
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 20),
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 20)
+ }
+ };
+ }
+
+ private class BeatmapList : CompositeDrawable
+ {
+ private readonly Container beatmapDisplays;
+ private readonly Ruleset ruleset;
+
+ public BeatmapList(Ruleset ruleset)
+ {
+ this.ruleset = ruleset;
+
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ InternalChild = beatmapDisplays = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 4)
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(BeatmapManager beatmaps)
+ {
+ var sets = beatmaps.GetAllUsableBeatmapSets();
+ var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID < 0 || b.RulesetID == ruleset.LegacyID);
+
+ allBeatmaps.ForEach(b => beatmapDisplays.Add(new BeatmapDisplay(b)));
+ }
+
+ private class BeatmapDisplay : CompositeDrawable, IHasTooltip
+ {
+ private readonly OsuSpriteText text;
+ private readonly BeatmapInfo beatmap;
+
+ private BeatmapManager beatmaps;
+ private OsuGameBase osuGame;
+
+ private bool isSelected;
+
+ public string TooltipText => text.Text;
+
+ public BeatmapDisplay(BeatmapInfo beatmap)
+ {
+ this.beatmap = beatmap;
+
+ AutoSizeAxes = Axes.Both;
+ InternalChild = text = new OsuSpriteText();
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ if (osuGame.Beatmap.Value.BeatmapInfo.ID == beatmap.ID)
+ return false;
+
+ osuGame.Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap);
+ isSelected = true;
+ return true;
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ if (isSelected)
+ return false;
+ this.FadeColour(Color4.Yellow, 100);
+ return true;
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ if (isSelected)
+ return;
+ this.FadeColour(Color4.White, 100);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase osuGame, BeatmapManager beatmaps)
+ {
+ this.osuGame = osuGame;
+ this.beatmaps = beatmaps;
+
+ var working = beatmaps.GetWorkingBeatmap(beatmap);
+ text.Text = $"{working.Metadata.Artist} - {working.Metadata.Title} ({working.Metadata.AuthorString}) [{working.BeatmapInfo.Version}]";
+
+ osuGame.Beatmap.ValueChanged += beatmapChanged;
+ }
+
+ private void beatmapChanged(WorkingBeatmap newBeatmap)
+ {
+ if (isSelected)
+ this.FadeColour(Color4.White, 100);
+ isSelected = false;
+ }
+ }
+ }
+
+ private class PerformanceList : CompositeDrawable
+ {
+ private readonly FillFlowContainer scores;
+ private APIAccess api;
+
+ public PerformanceList()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ InternalChild = scores = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 4)
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase osuGame, APIAccess api)
+ {
+ this.api = api;
+
+ if (!api.IsLoggedIn)
+ {
+ InternalChild = new SpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = "Please login to see online scores",
+ };
+ }
+
+ osuGame.Beatmap.ValueChanged += beatmapChanged;
+ }
+
+ private GetScoresRequest lastRequest;
+ private void beatmapChanged(WorkingBeatmap newBeatmap)
+ {
+ lastRequest?.Cancel();
+ scores.Clear();
+
+ if (!api.IsLoggedIn)
+ return;
+
+ lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo);
+ lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new PerformanceDisplay(s, newBeatmap.Beatmap)));
+ api.Queue(lastRequest);
+ }
+
+ private class PerformanceDisplay : CompositeDrawable
+ {
+ private readonly OsuSpriteText text;
+
+ private readonly Score score;
+ private readonly Beatmap beatmap;
+
+ public PerformanceDisplay(Score score, Beatmap beatmap)
+ {
+ this.score = score;
+ this.beatmap = beatmap;
+
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ InternalChild = text = new OsuSpriteText();
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance();
+ var calculator = ruleset.CreatePerformanceCalculator(beatmap, score);
+ if (calculator == null)
+ return;
+
+ var attributes = new Dictionary();
+ double performance = calculator.Calculate(attributes);
+
+ text.Text = $"{score.User.Username} -> online: {score.PP:n2}pp | local: {performance:n2}pp";
+ }
+ }
+ }
+
+ private class StarRatingGrid : CompositeDrawable
+ {
+ private readonly FillFlowContainer modFlow;
+ private readonly OsuSpriteText totalText;
+ private readonly FillFlowContainer categoryTexts;
+
+ public StarRatingGrid()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ InternalChild = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ modFlow = new FillFlowContainer
+ {
+ Name = "Checkbox flow",
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(4, 4)
+ },
+ new FillFlowContainer
+ {
+ Name = "Information display",
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(0, 4),
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ totalText = new OsuSpriteText { TextSize = 24 },
+ categoryTexts = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical
+ }
+ }
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase osuGame)
+ {
+ osuGame.Beatmap.ValueChanged += beatmapChanged;
+ }
+
+ private Cached informationCache = new Cached();
+
+ private Ruleset ruleset;
+ private WorkingBeatmap beatmap;
+
+ private void beatmapChanged(WorkingBeatmap newBeatmap)
+ {
+ beatmap = newBeatmap;
+
+ modFlow.Clear();
+
+ ruleset = newBeatmap.BeatmapInfo.Ruleset.CreateInstance();
+ foreach (var mod in ruleset.GetAllMods())
+ {
+ var checkBox = new OsuCheckbox
+ {
+ RelativeSizeAxes = Axes.None,
+ Width = 50,
+ LabelText = mod.ShortenedName
+ };
+
+ checkBox.Current.ValueChanged += v => informationCache.Invalidate();
+ modFlow.Add(checkBox);
+ }
+
+ informationCache.Invalidate();
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (ruleset == null)
+ return;
+
+ if (!informationCache.IsValid)
+ {
+ totalText.Text = string.Empty;
+ categoryTexts.Clear();
+
+ var allMods = ruleset.GetAllMods().ToList();
+ Mod[] activeMods = modFlow.Where(c => c.Current.Value).Select(c => allMods.First(m => m.ShortenedName == c.LabelText)).ToArray();
+
+ var diffCalc = ruleset.CreateDifficultyCalculator(beatmap.Beatmap, activeMods);
+ if (diffCalc != null)
+ {
+ var categories = new Dictionary();
+ double totalSr = diffCalc.Calculate(categories);
+
+ totalText.Text = $"Star rating: {totalSr:n2}";
+ foreach (var kvp in categories)
+ categoryTexts.Add(new OsuSpriteText { Text = $"{kvp.Key}: {kvp.Value:n2}" });
+ }
+
+ informationCache.Validate();
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs
index 5965be9717..d9951e002b 100644
--- a/osu.Game/Tests/Visual/TestCasePlayer.cs
+++ b/osu.Game/Tests/Visual/TestCasePlayer.cs
@@ -17,14 +17,12 @@ using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
- public abstract class TestCasePlayer : OsuTestCase
+ public abstract class TestCasePlayer : ScreenTestCase
{
private readonly Type ruleset;
protected Player Player;
- public override string Description => @"Showing everything to play the game.";
-
///
/// Create a TestCase which runs through the Player screen.
///
@@ -46,12 +44,17 @@ namespace osu.Game.Tests.Visual
{
RelativeSizeAxes = Framework.Graphics.Axes.Both,
Colour = Color4.Black,
+ Depth = int.MaxValue
});
string instantiation = ruleset?.AssemblyQualifiedName;
foreach (var r in rulesets.AvailableRulesets.Where(rs => instantiation == null || rs.InstantiationInfo == instantiation))
- AddStep(r.Name, () => loadPlayerFor(r));
+ {
+ Player p = null;
+ AddStep(r.Name, () => p = loadPlayerFor(r));
+ AddUntilStep(() => p.IsLoaded);
+ }
}
protected virtual Beatmap CreateBeatmap()
@@ -65,7 +68,7 @@ namespace osu.Game.Tests.Visual
return beatmap;
}
- private void loadPlayerFor(RulesetInfo r)
+ private Player loadPlayerFor(RulesetInfo r)
{
var beatmap = CreateBeatmap();
@@ -79,19 +82,21 @@ namespace osu.Game.Tests.Visual
if (Player != null)
Remove(Player);
- Add(Player = CreatePlayer(working, instance));
+ var player = CreatePlayer(working, instance);
+
+ LoadComponentAsync(player, LoadScreen);
+
+ return player;
}
- protected virtual Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
+ protected virtual Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) => new Player
{
- return new Player
- {
- InitialBeatmap = beatmap
- };
- }
+ InitialBeatmap = beatmap,
+ AllowPause = false
+ };
private const string test_beatmap_data =
-@"osu file format v14
+ @"osu file format v14
[General]
AudioLeadIn: 500
diff --git a/osu.Game/Users/UpdateableAvatar.cs b/osu.Game/Users/UpdateableAvatar.cs
index 7c020fce91..d55c0caad7 100644
--- a/osu.Game/Users/UpdateableAvatar.cs
+++ b/osu.Game/Users/UpdateableAvatar.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Users
///
public class UpdateableAvatar : Container
{
- private Container displayedAvatar;
+ private Drawable displayedAvatar;
private User user;
@@ -40,11 +40,13 @@ namespace osu.Game.Users
{
displayedAvatar?.FadeOut(300);
displayedAvatar?.Expire();
- Add(displayedAvatar = new DelayedLoadWrapper(new Avatar(user)
- {
- RelativeSizeAxes = Axes.Both,
- OnLoadComplete = d => d.FadeInFromZero(200),
- }));
+ Add(displayedAvatar = new DelayedLoadWrapper(
+ new Avatar(user)
+ {
+ RelativeSizeAxes = Axes.Both,
+ OnLoadComplete = d => d.FadeInFromZero(200),
+ })
+ );
}
}
}
diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs
index ab4d55027d..d056afcf54 100644
--- a/osu.Game/Users/UserPanel.cs
+++ b/osu.Game/Users/UserPanel.cs
@@ -17,10 +17,11 @@ using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Backgrounds;
+using osu.Game.Graphics.Containers;
namespace osu.Game.Users
{
- public class UserPanel : ClickableContainer, IHasContextMenu
+ public class UserPanel : OsuClickableContainer, IHasContextMenu
{
private readonly User user;
private const float height = 100;
@@ -58,14 +59,14 @@ namespace osu.Game.Users
Children = new Drawable[]
{
- new AsyncLoadWrapper(new UserCoverBackground(user)
+ new DelayedLoadWrapper(new UserCoverBackground(user)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
OnLoadComplete = d => d.FadeInFromZero(200),
- }) { RelativeSizeAxes = Axes.Both },
+ }, 0) { RelativeSizeAxes = Axes.Both },
new Box
{
RelativeSizeAxes = Axes.Both,